From c863107293b314afde8495608bd8869b1e068946 Mon Sep 17 00:00:00 2001 From: chapeau Date: Tue, 4 Feb 2020 10:54:17 +0000 Subject: [PATCH 001/490] Add ip_type in dhcp api serializer --- api/serializers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/serializers.py b/api/serializers.py index ec4d0689..a2722d2a 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1168,11 +1168,12 @@ class HostMacIpSerializer(serializers.ModelSerializer): hostname = serializers.CharField(source="domain.name", read_only=True) extension = serializers.CharField(source="domain.extension.name", read_only=True) mac_address = serializers.CharField(read_only=True) + ip_type = serializers.CharField(source="machine_type.ip_type", read_only=True) ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) class Meta: model = machines.Interface - fields = ("hostname", "extension", "mac_address", "ipv4") + fields = ("hostname", "extension", "mac_address", "ipv4", "ip_type") # DNS From fe11867ba973d74d105f0cae554ceb3c557bee9f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 16 Feb 2020 18:35:46 +0000 Subject: [PATCH 002/490] Add ability to search by building+room --- search/views.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/search/views.py b/search/views.py index 3eb9deae..b07f079d 100644 --- a/search/views.py +++ b/search/views.py @@ -121,6 +121,12 @@ def search_single_word(word, filters, user, start, end, user_state, aff): ) filter_users = (filter_clubs | Q(name__icontains=word)) + if len(word.split(" ")) >= 2: + # Assume the room is in 1 word, and the building may be in multiple words + building = " ".join(word.split(" ")[:-1]) + room = word.split(" ")[-1] + filter_users |= (Q(room__name__icontains=room) & Q(room__building__name__icontains=building)) + if not User.can_view_all(user)[0]: filter_clubs &= Q(id=user.id) filter_users &= Q(id=user.id) @@ -204,6 +210,13 @@ def search_single_word(word, filters, user, start, end, user_state, aff): filter_rooms = ( Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) ) + + if len(word.split(" ")) >= 2: + # Assume the room is in 1 word, and the building may be in multiple words + building = " ".join(word.split(" ")[:-1]) + room = word.split(" ")[-1] + filter_rooms |= (Q(name__icontains=room) & Q(building__name__icontains=building)) + filters["rooms"] |= filter_rooms # Switch ports @@ -326,6 +339,10 @@ def get_words(query): # If we are between two ", ignore separators words[i] += char continue + if char == "+": + # If we encouter a + outside of ", we replace it with a space + words[i] += " " + continue if char == " " or char == ",": # If we encouter a separator outside of ", we create a new word if words[i] is not "": From 5eac7f6614c446cdd51a884b55dd8a3a62d7b980 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 12:02:08 +0100 Subject: [PATCH 003/490] Handle queries with "+" operators --- search/views.py | 143 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 98 insertions(+), 45 deletions(-) diff --git a/search/views.py b/search/views.py index b07f079d..4ea4814e 100644 --- a/search/views.py +++ b/search/views.py @@ -62,11 +62,22 @@ def is_int(variable): return True +def filter_fields(): + """Return the list of fields the search applies to""" + return ["users", "clubs", "machines", "factures", "bans", "whitelists", "rooms", "ports", "switches"] + + +def empty_filters(): + """Build empty filters used by Django""" + filters = [Q() for f in filter_fields()] + + def finish_results(request, 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""" + results["users"] += results["clubs"] results["users"] = SortTable.sort( results["users"], col, order, SortTable.USERS_INDEX ) @@ -121,12 +132,6 @@ def search_single_word(word, filters, user, start, end, user_state, aff): ) filter_users = (filter_clubs | Q(name__icontains=word)) - if len(word.split(" ")) >= 2: - # Assume the room is in 1 word, and the building may be in multiple words - building = " ".join(word.split(" ")[:-1]) - room = word.split(" ")[-1] - filter_users |= (Q(room__name__icontains=room) & Q(room__building__name__icontains=building)) - if not User.can_view_all(user)[0]: filter_clubs &= Q(id=user.id) filter_users &= Q(id=user.id) @@ -211,12 +216,6 @@ def search_single_word(word, filters, user, start, end, user_state, aff): Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) ) - if len(word.split(" ")) >= 2: - # Assume the room is in 1 word, and the building may be in multiple words - building = " ".join(word.split(" ")[:-1]) - room = word.split(" ")[-1] - filter_rooms |= (Q(name__icontains=room) & Q(building__name__icontains=building)) - filters["rooms"] |= filter_rooms # Switch ports @@ -252,7 +251,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff): def apply_filters(filters, user, aff): - """ Apply the filters constructed by search_single_word. + """ Apply the filters constructed by search_single_query. It also takes into account the visual filters defined during the search query. """ @@ -305,53 +304,117 @@ def apply_filters(filters, user, aff): return results -def get_words(query): - """Function used to split the uery in different words to look for. - The rules are simple : +def search_single_query(query, filters, user, start, end, user_state, aff): + """ Handle different queries an construct the correct filters using + search_single_word""" + if query["operator"] == "+": + # Special queries with "+" operators should use & rather than | + for q in query["subqueries"]: + # Construct an independent filter for each subquery + subfilters = empty_filters() + subfilters = search_single_word(q, subfilters, user, start, end, user_state, aff) + + # Apply the new filter + for field in filter_fields(): + filters[field] &= subfilters[field] + + return filters + + # Handle standard queries + q = query["text"] + return search_single_word(q, filters, user, start, end, user_state, aff) + + +def create_queries(query): + """Function used to split the query in different words to look for. + The rules are the following : - anti-slash ('\\') is used to escape characters - anything between quotation marks ('"') is kept intact (not interpreted as separators) excepts anti-slashes used to escape + Values in between quotation marks are not searched accross + multiple field in the database (contrary to +) - spaces (' ') and commas (',') are used to separated words + - "+" signs are used as "and" operators """ + # A dict representing the different queries extracted from the user's text + queries = [] - words = [] - i = 0 + # Format: { + # "text": "", # Content of the query + # "operator": None, # Whether a special char ("+") was used + # "subqueries": None # When splitting the query in subparts (ex when using "+") + # } + current_query = None + + # Whether the query is between " keep_intact = False + + # Whether the previous char was a \ escaping_char = False + for char in query: - if i >= len(words): + if current_query is None: # We are starting a new word - words.append("") + current_query = { "text": "", "operator": None, "subqueries": None } + if escaping_char: # The last char war a \ so we escape this char escaping_char = False - words[i] += char + current_query["text"] += 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 + current_query["text"] += char continue + if char == "+": - # If we encouter a + outside of ", we replace it with a space - words[i] += " " + # Can't sart a query with a "+", consider it escaped + if len(current_query["text"]) == 0: + current_query["text"] = char + continue + + # Build a slightly more complicate data structure + # This is need for queries like '"A B"+C' + if current_query["operator"] is None: + current_query["operator"] = "+" + current_query["subqueries"] = [] + + current_query["subqueries"].append(current_query["text"]) + current_query["text"] = "" 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 + if len(current_query["text"]) == 0: + # Discard empty queries + continue + + if current_query["operator"] is not None: + # If we were building a special structure, finish building it + current_query["subqueries"].append(current_query["text"]) + current_query["text"] = "" + + # Save the query and start a new one + queries.append(current_query) + current_query = None + continue + + # If we haven't encountered any special case, add the char to the word + current_query["text"].append(char) + + return queries def get_results(query, request, params): @@ -365,22 +428,12 @@ def get_results(query, request, params): user_state = params.get("u", initial_choices(CHOICES_USER)) aff = params.get("a", initial_choices(CHOICES_AFF)) - filters = { - "users": Q(), - "clubs": Q(), - "machines": Q(), - "factures": Q(), - "bans": Q(), - "whitelists": Q(), - "rooms": Q(), - "ports": Q(), - "switches": Q(), - } + filters = empty_filters() - words = get_words(query) - for word in words: - filters = search_single_word( - word, filters, request.user, start, end, user_state, aff + queries = create_queries(query) + for query in queries: + filters = search_single_query( + query, filters, request.user, start, end, user_state, aff ) results = apply_filters(filters, request.user, aff) From e51038c07ba846eeb2122be53a35a18052cafb10 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 14:29:47 +0100 Subject: [PATCH 004/490] Create Query class --- search/views.py | 99 ++++++++++++++++++++++++++++++------------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/search/views.py b/search/views.py index 4ea4814e..30ef9bb0 100644 --- a/search/views.py +++ b/search/views.py @@ -51,6 +51,25 @@ from re2o.base import SortTable, re2o_paginator from re2o.acl import can_view_all +class Query: + def __init__(self, text=""): + self.text = text # Content of the query + self.operator = None # Whether a special char (ex "+") was used + self.subqueries = None # When splitting the query in subparts (ex when using "+") + + def add_char(self, char): + self.text += char + + def add_operator(self, operator): + self.operator = operator + + if self.subqueries is None: + self.subqueries = [] + + self.subqueries.append(Query(self.text)) + self.text = "" + + def is_int(variable): """ Check if the variable can be casted to an integer """ @@ -69,7 +88,7 @@ def filter_fields(): def empty_filters(): """Build empty filters used by Django""" - filters = [Q() for f in filter_fields()] + return {f: Q() for f in filter_fields()} def finish_results(request, results, col, order): @@ -77,10 +96,12 @@ def finish_results(request, results, col, order): number of max results. Finally add the info of the nmax number of results to the dict""" - results["users"] += results["clubs"] results["users"] = SortTable.sort( results["users"], col, order, SortTable.USERS_INDEX ) + results["clubs"] = SortTable.sort( + results["clubs"], col, order, SortTable.USERS_INDEX + ) results["machines"] = SortTable.sort( results["machines"], col, order, SortTable.MACHINES_INDEX ) @@ -129,6 +150,8 @@ def search_single_word(word, filters, user, start, end, user_state, aff): | Q(room__name__icontains=word) | Q(email__icontains=word) | Q(telephone__icontains=word) + | Q(room__name__icontains=word) + | Q(room__building__name__icontains=word) ) filter_users = (filter_clubs | Q(name__icontains=word)) @@ -213,8 +236,9 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Rooms if "5" in aff and Room.can_view_all(user): filter_rooms = ( - Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) + Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=building) ) + filter_rooms |= (Q(name__icontains=room) & Q(building__name__icontains=building)) filters["rooms"] |= filter_rooms @@ -307,22 +331,25 @@ def apply_filters(filters, user, aff): def search_single_query(query, filters, user, start, end, user_state, aff): """ Handle different queries an construct the correct filters using search_single_word""" - if query["operator"] == "+": + if query.operator == "+": # Special queries with "+" operators should use & rather than | - for q in query["subqueries"]: + newfilters = empty_filters() + for q in query.subqueries: # Construct an independent filter for each subquery - subfilters = empty_filters() - subfilters = search_single_word(q, subfilters, user, start, end, user_state, aff) + subfilters = search_single_query(q, empty_filters(), user, start, end, user_state, aff) - # Apply the new filter + # Apply the subfilter for field in filter_fields(): - filters[field] &= subfilters[field] + newfilters[field] &= subfilters[field] + + # Add these filters to the existing ones + for field in filter_fields(): + filters[field] |= newfilters[field] return filters # Handle standard queries - q = query["text"] - return search_single_word(q, filters, user, start, end, user_state, aff) + return search_single_word(query.text, filters, user, start, end, user_state, aff) def create_queries(query): @@ -338,12 +365,6 @@ def create_queries(query): """ # A dict representing the different queries extracted from the user's text queries = [] - - # Format: { - # "text": "", # Content of the query - # "operator": None, # Whether a special char ("+") was used - # "subqueries": None # When splitting the query in subparts (ex when using "+") - # } current_query = None # Whether the query is between " @@ -355,12 +376,12 @@ def create_queries(query): for char in query: if current_query is None: # We are starting a new word - current_query = { "text": "", "operator": None, "subqueries": None } + current_query = Query() if escaping_char: # The last char war a \ so we escape this char escaping_char = False - current_query["text"] += char + current_query.add_char(char) continue if char == "\\": @@ -375,36 +396,28 @@ def create_queries(query): if keep_intact: # If we are between two ", ignore separators - current_query["text"] += char + current_query.add_char(char) continue if char == "+": - # Can't sart a query with a "+", consider it escaped - if len(current_query["text"]) == 0: - current_query["text"] = char + if len(current_query.text) == 0: + # Can't sart a query with a "+", consider it escaped + current_query.add_char(char) continue - # Build a slightly more complicate data structure - # This is need for queries like '"A B"+C' - if current_query["operator"] is None: - current_query["operator"] = "+" - current_query["subqueries"] = [] - - current_query["subqueries"].append(current_query["text"]) - current_query["text"] = "" + current_query.add_operator("+") continue if char == " " or char == ",": # If we encouter a separator outside of ", we create a new word - if len(current_query["text"]) == 0: + if len(current_query.text) == 0: # Discard empty queries continue - if current_query["operator"] is not None: + if current_query.operator is not None: # If we were building a special structure, finish building it - current_query["subqueries"].append(current_query["text"]) - current_query["text"] = "" + current_query.add_operator(current_query.operator) # Save the query and start a new one queries.append(current_query) @@ -412,7 +425,17 @@ def create_queries(query): continue # If we haven't encountered any special case, add the char to the word - current_query["text"].append(char) + current_query.add_char(char) + + # Save the current working query if necessary + if current_query is not None: + if current_query.operator is not None: + # There was an operator supposed to split multiple words + if len(current_query.text) > 0: + # Finish the current search + current_query.add_operator(current_query.operator) + + queries.append(current_query) return queries @@ -431,9 +454,9 @@ def get_results(query, request, params): filters = empty_filters() queries = create_queries(query) - for query in queries: + for q in queries: filters = search_single_query( - query, filters, request.user, start, end, user_state, aff + q, filters, request.user, start, end, user_state, aff ) results = apply_filters(filters, request.user, aff) From 8a2a13748f27e4c4eeee655861dd7a545795a7f9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 14:01:48 +0000 Subject: [PATCH 005/490] Add plaintext representation for Query --- search/views.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/search/views.py b/search/views.py index 30ef9bb0..c2e9338f 100644 --- a/search/views.py +++ b/search/views.py @@ -69,6 +69,13 @@ class Query: self.subqueries.append(Query(self.text)) self.text = "" + @property + def plaintext(self): + if self.operator is not None: + return self.operator.join([q.plaintext for q in self.subqueries]) + + return self.text + def is_int(variable): """ Check if the variable can be casted to an integer """ @@ -236,9 +243,8 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Rooms if "5" in aff and Room.can_view_all(user): filter_rooms = ( - Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=building) + Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=word) ) - filter_rooms |= (Q(name__icontains=room) & Q(building__name__icontains=building)) filters["rooms"] |= filter_rooms From 2180e9de42c7cc4a1b8e4268eca968f6bbffacbc Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 16:04:54 +0100 Subject: [PATCH 006/490] Update description for advanced search --- search/forms.py | 4 ++-- search/locale/fr/LC_MESSAGES/django.po | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/search/forms.py b/search/forms.py index 259f739c..1cfd02cb 100644 --- a/search/forms.py +++ b/search/forms.py @@ -63,7 +63,7 @@ class SearchForm(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search and «\\» to escape a character." + " an exact search, «\\» to escape a character and «+» to combine keywors." ) ), max_length=100, @@ -78,7 +78,7 @@ class SearchFormPlus(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search and «\\» to escape a character." + " an exact search, «\\» to escape a character and «+» to combine keywors." ) ), max_length=100, diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 6c317169..efb33ebe 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -90,10 +90,10 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" "Use « » and «,» to specify distinct words, «\"query\"» for an exact search " -"and «\\» to escape a character." +"an exact search, «\\» to escape a character and «+» to combine keywors." msgstr "" -"Utilisez « » et «,» pour spécifier différents mots, «\"query\"» pour une " -"recherche exacte et «\\» pour échapper un caractère." +"Utilisez « » et «,» pour spécifier différents mots, «\"mot\"» pour une " +"recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des mots clés." #: search/forms.py:88 msgid "Users filter" From 738c4d669ce4b152e967e50b92cc21808e256d99 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 18:16:08 +0100 Subject: [PATCH 007/490] Split search/views into 2 files --- search/engine.py | 448 +++++++++++++++++++++++++++++++++++++++++++++++ search/views.py | 406 +----------------------------------------- 2 files changed, 451 insertions(+), 403 deletions(-) create mode 100644 search/engine.py diff --git a/search/engine.py b/search/engine.py new file mode 100644 index 00000000..716cacd6 --- /dev/null +++ b/search/engine.py @@ -0,0 +1,448 @@ +# 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 Lara Kermarec +# Copyright © 2017 Augustin Lemesle +# Copyright © 2019 Jean-Romain Garnier +# +# 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. + +"""The views for the search app, responsible for finding the matches +Augustin lemesle, Gabriel Détraz, Lara Kermarec, Maël Kervella, +Jean-Romain Garnier +Gplv2""" + +from __future__ import unicode_literals + +from netaddr import EUI, AddrFormatError + +from django.db.models import Q +from users.models import User, Adherent, Club, Ban, Whitelist +from machines.models import Machine +from topologie.models import Port, Switch, Room +from cotisations.models import Facture +from preferences.models import GeneralOption +from re2o.base import SortTable, re2o_paginator + + +class Query: + """Class representing a query. + It can contain the user-entered text, the operator for the query, + and a list of subqueries""" + def __init__(self, text=""): + self.text = text # Content of the query + self.operator = None # Whether a special char (ex "+") was used + self.subqueries = None # When splitting the query in subparts + + def add_char(self, char): + """Add the given char to the query's text""" + self.text += char + + def add_operator(self, operator): + """Consider a new operator was entered, and that it must be processed. + The query's current text is moved to self.subqueries in the form + of a plain Query object""" + self.operator = operator + + if self.subqueries is None: + self.subqueries = [] + + self.subqueries.append(Query(self.text)) + self.text = "" + + @property + def plaintext(self): + """Returns a textual representation of the query's content""" + if self.operator is not None: + return self.operator.join([q.plaintext for q in self.subqueries]) + + return self.text + + +def filter_fields(): + """Return the list of fields the search applies to""" + return ["users", + "clubs", + "machines", + "factures", + "bans", + "whitelists", + "rooms", + "ports", + "switches"] + + +def empty_filters(): + """Build empty filters used by Django""" + return {f: Q() for f in filter_fields()} + + +def is_int(variable): + """ Check if the variable can be casted to an integer """ + try: + int(variable) + except ValueError: + return False + else: + return True + + +def finish_results(request, 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""" + results["users"] = SortTable.sort( + results["users"], col, order, SortTable.USERS_INDEX + ) + results["clubs"] = SortTable.sort( + results["clubs"], 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 + ) + + max_result = GeneralOption.get_cached_value("search_display_page") + for name, val in results.items(): + page_arg = name + "_page" + results[name] = re2o_paginator(request, val.distinct(), max_result, page_arg=page_arg) + + results.update({"max_result": max_result}) + + return results + + +def search_single_word(word, filters, user, 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_clubs = ( + Q(surname__icontains=word) + | Q(pseudo__icontains=word) + | Q(room__name__icontains=word) + | Q(email__icontains=word) + | Q(telephone__icontains=word) + | Q(room__name__icontains=word) + | Q(room__building__name__icontains=word) + ) + filter_users = (filter_clubs | Q(name__icontains=word)) + + if not User.can_view_all(user)[0]: + filter_clubs &= Q(id=user.id) + filter_users &= Q(id=user.id) + + filter_clubs &= Q(state__in=user_state) + filter_users &= Q(state__in=user_state) + + filters["users"] |= filter_users + filters["clubs"] |= filter_clubs + + # 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) + ) + try: + _mac_addr = EUI(word, 48) + filter_machines |= Q(interface__mac_address=word) + except AddrFormatError: + pass + if not Machine.can_view_all(user)[0]: + 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 Room.can_view_all(user): + filter_rooms = ( + Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=word) + ) + + filters["rooms"] |= filter_rooms + + # Switch ports + if "6" in aff and User.can_view_all(user): + filter_ports = ( + Q(room__name__icontains=word) + | Q(machine_interface__domain__name__icontains=word) + | Q(related__switch__interface__domain__name__icontains=word) + | Q(custom_profile__name__icontains=word) + | Q(custom_profile__profil_default__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 Switch.can_view_all(user): + filter_switches = ( + Q(interface__domain__name__icontains=word) + | Q(interface__ipv4__ipv4__icontains=word) + | Q(switchbay__building__name__icontains=word) + | Q(stack__name__icontains=word) + | Q(model__reference__icontains=word) + | Q(model__constructor__name__icontains=word) + | Q(interface__details__icontains=word) + ) + if is_int(word): + filter_switches |= Q(number=word) | Q(stack_member_id=word) + filters["switches"] |= filter_switches + + return filters + + +def apply_filters(filters, user, aff): + """ Apply the filters constructed by search_single_query. + It also takes into account the visual filters defined during + the search query. + """ + # Results are later filled-in depending on the display filter + results = { + "users": Adherent.objects.none(), + "clubs": Club.objects.none(), + "machines": Machine.objects.none(), + "factures": Facture.objects.none(), + "bans": Ban.objects.none(), + "whitelists": Whitelist.objects.none(), + "rooms": Room.objects.none(), + "ports": Port.objects.none(), + "switches": Switch.objects.none(), + } + + # Users and clubs + if "0" in aff: + results["users"] = Adherent.objects.filter(filters["users"]) + results["clubs"] = Club.objects.filter(filters["clubs"]) + + # Machines + if "1" in aff: + results["machines"] = Machine.objects.filter(filters["machines"]) + + # Factures + if "2" in aff: + results["factures"] = Facture.objects.filter(filters["factures"]) + + # Bans + if "3" in aff: + results["bans"] = Ban.objects.filter(filters["bans"]) + + # Whitelists + if "4" in aff: + results["whitelists"] = Whitelist.objects.filter(filters["whitelists"]) + + # Rooms + if "5" in aff and Room.can_view_all(user): + results["rooms"] = Room.objects.filter(filters["rooms"]) + + # Switch ports + if "6" in aff and User.can_view_all(user): + results["ports"] = Port.objects.filter(filters["ports"]) + + # Switches + if "7" in aff and Switch.can_view_all(user): + results["switches"] = Switch.objects.filter(filters["switches"]) + + return results + + +def search_single_query(query, filters, user, start, end, user_state, aff): + """ Handle different queries an construct the correct filters using + search_single_word""" + if query.operator == "+": + # Special queries with "+" operators should use & rather than | + newfilters = empty_filters() + for q in query.subqueries: + # Construct an independent filter for each subquery + subfilters = search_single_query(q, empty_filters(), user, start, end, user_state, aff) + + # Apply the subfilter + for field in filter_fields(): + newfilters[field] &= subfilters[field] + + # Add these filters to the existing ones + for field in filter_fields(): + filters[field] |= newfilters[field] + + return filters + + # Handle standard queries + return search_single_word(query.text, filters, user, start, end, user_state, aff) + + +def create_queries(query): + """Function used to split the query in different words to look for. + The rules are the following : + - anti-slash ('\\') is used to escape characters + - anything between quotation marks ('"') is kept intact (not + interpreted as separators) excepts anti-slashes used to escape + Values in between quotation marks are not searched accross + multiple field in the database (contrary to +) + - spaces (' ') and commas (',') are used to separated words + - "+" signs are used as "and" operators + """ + # A dict representing the different queries extracted from the user's text + queries = [] + current_query = None + + # Whether the query is between " + keep_intact = False + + # Whether the previous char was a \ + escaping_char = False + + for char in query: + if current_query is None: + # We are starting a new word + current_query = Query() + + if escaping_char: + # The last char war a \ so we escape this char + escaping_char = False + current_query.add_char(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 + current_query.add_char(char) + continue + + if char == "+": + if len(current_query.text) == 0: + # Can't sart a query with a "+", consider it escaped + current_query.add_char(char) + continue + + current_query.add_operator("+") + continue + + if char == " " or char == ",": + # If we encouter a separator outside of ", we create a new word + + if len(current_query.text) == 0: + # Discard empty queries + continue + + if current_query.operator is not None: + # If we were building a special structure, finish building it + current_query.add_operator(current_query.operator) + + # Save the query and start a new one + queries.append(current_query) + current_query = None + continue + + # If we haven't encountered any special case, add the char to the word + current_query.add_char(char) + + # Save the current working query if necessary + if current_query is not None: + if current_query.operator is not None: + # There was an operator supposed to split multiple words + if len(current_query.text) > 0: + # Finish the current search + current_query.add_operator(current_query.operator) + + queries.append(current_query) + + return queries diff --git a/search/views.py b/search/views.py index c2e9338f..cf0a5248 100644 --- a/search/views.py +++ b/search/views.py @@ -28,18 +28,12 @@ Gplv2""" from __future__ import unicode_literals -from netaddr import EUI, AddrFormatError - from django.shortcuts import render from django.contrib.auth.decorators import login_required -from django.db.models import Q -from users.models import User, Adherent, Club, Ban, Whitelist -from machines.models import Machine +from users.models import User from cotisations.models import Cotisation -from topologie.models import Port, Switch, Room -from cotisations.models import Facture -from preferences.models import GeneralOption +from machines.models import Machine from search.forms import ( SearchForm, SearchFormPlus, @@ -47,403 +41,9 @@ from search.forms import ( CHOICES_AFF, initial_choices, ) -from re2o.base import SortTable, re2o_paginator from re2o.acl import can_view_all - -class Query: - def __init__(self, text=""): - self.text = text # Content of the query - self.operator = None # Whether a special char (ex "+") was used - self.subqueries = None # When splitting the query in subparts (ex when using "+") - - def add_char(self, char): - self.text += char - - def add_operator(self, operator): - self.operator = operator - - if self.subqueries is None: - self.subqueries = [] - - self.subqueries.append(Query(self.text)) - self.text = "" - - @property - def plaintext(self): - if self.operator is not None: - return self.operator.join([q.plaintext for q in self.subqueries]) - - return self.text - - -def is_int(variable): - """ Check if the variable can be casted to an integer """ - - try: - int(variable) - except ValueError: - return False - else: - return True - - -def filter_fields(): - """Return the list of fields the search applies to""" - return ["users", "clubs", "machines", "factures", "bans", "whitelists", "rooms", "ports", "switches"] - - -def empty_filters(): - """Build empty filters used by Django""" - return {f: Q() for f in filter_fields()} - - -def finish_results(request, 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""" - - results["users"] = SortTable.sort( - results["users"], col, order, SortTable.USERS_INDEX - ) - results["clubs"] = SortTable.sort( - results["clubs"], 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 - ) - - max_result = GeneralOption.get_cached_value("search_display_page") - for name, val in results.items(): - page_arg = name + "_page" - results[name] = re2o_paginator(request, val.distinct(), max_result, page_arg=page_arg) - - results.update({"max_result": max_result}) - - return results - - -def search_single_word(word, filters, user, 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_clubs = ( - Q(surname__icontains=word) - | Q(pseudo__icontains=word) - | Q(room__name__icontains=word) - | Q(email__icontains=word) - | Q(telephone__icontains=word) - | Q(room__name__icontains=word) - | Q(room__building__name__icontains=word) - ) - filter_users = (filter_clubs | Q(name__icontains=word)) - - if not User.can_view_all(user)[0]: - filter_clubs &= Q(id=user.id) - filter_users &= Q(id=user.id) - - filter_clubs &= Q(state__in=user_state) - filter_users &= Q(state__in=user_state) - - filters["users"] |= filter_users - filters["clubs"] |= filter_clubs - - # 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) - ) - try: - _mac_addr = EUI(word, 48) - filter_machines |= Q(interface__mac_address=word) - except AddrFormatError: - pass - if not Machine.can_view_all(user)[0]: - 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 Room.can_view_all(user): - filter_rooms = ( - Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=word) - ) - - filters["rooms"] |= filter_rooms - - # Switch ports - if "6" in aff and User.can_view_all(user): - filter_ports = ( - Q(room__name__icontains=word) - | Q(machine_interface__domain__name__icontains=word) - | Q(related__switch__interface__domain__name__icontains=word) - | Q(custom_profile__name__icontains=word) - | Q(custom_profile__profil_default__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 Switch.can_view_all(user): - filter_switches = ( - Q(interface__domain__name__icontains=word) - | Q(interface__ipv4__ipv4__icontains=word) - | Q(switchbay__building__name__icontains=word) - | Q(stack__name__icontains=word) - | Q(model__reference__icontains=word) - | Q(model__constructor__name__icontains=word) - | Q(interface__details__icontains=word) - ) - if is_int(word): - filter_switches |= Q(number=word) | Q(stack_member_id=word) - filters["switches"] |= filter_switches - - return filters - - -def apply_filters(filters, user, aff): - """ Apply the filters constructed by search_single_query. - It also takes into account the visual filters defined during - the search query. - """ - # Results are later filled-in depending on the display filter - results = { - "users": Adherent.objects.none(), - "clubs": Club.objects.none(), - "machines": Machine.objects.none(), - "factures": Facture.objects.none(), - "bans": Ban.objects.none(), - "whitelists": Whitelist.objects.none(), - "rooms": Room.objects.none(), - "ports": Port.objects.none(), - "switches": Switch.objects.none(), - } - - # Users and clubs - if "0" in aff: - results["users"] = Adherent.objects.filter(filters["users"]) - results["clubs"] = Club.objects.filter(filters["clubs"]) - - # Machines - if "1" in aff: - results["machines"] = Machine.objects.filter(filters["machines"]) - - # Factures - if "2" in aff: - results["factures"] = Facture.objects.filter(filters["factures"]) - - # Bans - if "3" in aff: - results["bans"] = Ban.objects.filter(filters["bans"]) - - # Whitelists - if "4" in aff: - results["whitelists"] = Whitelist.objects.filter(filters["whitelists"]) - - # Rooms - if "5" in aff and Room.can_view_all(user): - results["rooms"] = Room.objects.filter(filters["rooms"]) - - # Switch ports - if "6" in aff and User.can_view_all(user): - results["ports"] = Port.objects.filter(filters["ports"]) - - # Switches - if "7" in aff and Switch.can_view_all(user): - results["switches"] = Switch.objects.filter(filters["switches"]) - - return results - - -def search_single_query(query, filters, user, start, end, user_state, aff): - """ Handle different queries an construct the correct filters using - search_single_word""" - if query.operator == "+": - # Special queries with "+" operators should use & rather than | - newfilters = empty_filters() - for q in query.subqueries: - # Construct an independent filter for each subquery - subfilters = search_single_query(q, empty_filters(), user, start, end, user_state, aff) - - # Apply the subfilter - for field in filter_fields(): - newfilters[field] &= subfilters[field] - - # Add these filters to the existing ones - for field in filter_fields(): - filters[field] |= newfilters[field] - - return filters - - # Handle standard queries - return search_single_word(query.text, filters, user, start, end, user_state, aff) - - -def create_queries(query): - """Function used to split the query in different words to look for. - The rules are the following : - - anti-slash ('\\') is used to escape characters - - anything between quotation marks ('"') is kept intact (not - interpreted as separators) excepts anti-slashes used to escape - Values in between quotation marks are not searched accross - multiple field in the database (contrary to +) - - spaces (' ') and commas (',') are used to separated words - - "+" signs are used as "and" operators - """ - # A dict representing the different queries extracted from the user's text - queries = [] - current_query = None - - # Whether the query is between " - keep_intact = False - - # Whether the previous char was a \ - escaping_char = False - - for char in query: - if current_query is None: - # We are starting a new word - current_query = Query() - - if escaping_char: - # The last char war a \ so we escape this char - escaping_char = False - current_query.add_char(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 - current_query.add_char(char) - continue - - if char == "+": - if len(current_query.text) == 0: - # Can't sart a query with a "+", consider it escaped - current_query.add_char(char) - continue - - current_query.add_operator("+") - continue - - if char == " " or char == ",": - # If we encouter a separator outside of ", we create a new word - - if len(current_query.text) == 0: - # Discard empty queries - continue - - if current_query.operator is not None: - # If we were building a special structure, finish building it - current_query.add_operator(current_query.operator) - - # Save the query and start a new one - queries.append(current_query) - current_query = None - continue - - # If we haven't encountered any special case, add the char to the word - current_query.add_char(char) - - # Save the current working query if necessary - if current_query is not None: - if current_query.operator is not None: - # There was an operator supposed to split multiple words - if len(current_query.text) > 0: - # Finish the current search - current_query.add_operator(current_query.operator) - - queries.append(current_query) - - return queries +from engine import * def get_results(query, request, params): From a5f0b2b72b3f31ef9548da3cff08d29f46f5ec1a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 17:18:44 +0000 Subject: [PATCH 008/490] Fix import error --- search/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/views.py b/search/views.py index cf0a5248..60d0a92f 100644 --- a/search/views.py +++ b/search/views.py @@ -43,7 +43,7 @@ from search.forms import ( ) from re2o.acl import can_view_all -from engine import * +from .engine import * def get_results(query, request, params): From 8b9d1bc3b3076de7b0eeb01dea9dbfaf29e5d8ae Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 18:52:50 +0100 Subject: [PATCH 009/490] Add ability to make queries case sensitive --- search/engine.py | 101 +++++++++++++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/search/engine.py b/search/engine.py index 716cacd6..7d4d8934 100644 --- a/search/engine.py +++ b/search/engine.py @@ -43,10 +43,11 @@ class Query: """Class representing a query. It can contain the user-entered text, the operator for the query, and a list of subqueries""" - def __init__(self, text=""): + def __init__(self, text="", case_sensitive=False): self.text = text # Content of the query self.operator = None # Whether a special char (ex "+") was used self.subqueries = None # When splitting the query in subparts + self.case_sensitive = case_sensitive def add_char(self, char): """Add the given char to the query's text""" @@ -61,8 +62,9 @@ class Query: if self.subqueries is None: self.subqueries = [] - self.subqueries.append(Query(self.text)) + self.subqueries.append(Query(self.text, self.case_sensitive)) self.text = "" + self.case_sensitive = False @property def plaintext(self): @@ -70,6 +72,9 @@ class Query: if self.operator is not None: return self.operator.join([q.plaintext for q in self.subqueries]) + if self.case_sensitive: + return "\"{}\"".format(self.text) + return self.text @@ -143,7 +148,18 @@ def finish_results(request, results, col, order): return results -def search_single_word(word, filters, user, start, end, user_state, aff): +def contains_filter(attribute, word, case_sensitive=False): + """Create a django model filtering whether the given attribute + contains the specified value.""" + if case_sensitive: + attr = "{}__{}".format(attribute, "contains") + else: + attr = "{}__{}".format(attribute, "icontains") + + return Q(**{attr: word}) + + +def search_single_word(word, filters, user, start, end, user_state, aff, case_sensitive=False): """ 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 @@ -154,15 +170,14 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Users if "0" in aff: filter_clubs = ( - Q(surname__icontains=word) - | Q(pseudo__icontains=word) - | Q(room__name__icontains=word) - | Q(email__icontains=word) - | Q(telephone__icontains=word) - | Q(room__name__icontains=word) - | Q(room__building__name__icontains=word) + contains_filter("surname", word, case_sensitive) + | contains_filter("pseudo", word, case_sensitive) + | contains_filter("email", word, case_sensitive) + | contains_filter("telephone", word, case_sensitive) + | contains_filter("room__name", word, case_sensitive) + | contains_filter("room__building__name", word, case_sensitive) ) - filter_users = (filter_clubs | Q(name__icontains=word)) + filter_users = (filter_clubs | contains_filter("name", word, case_sensitive)) if not User.can_view_all(user)[0]: filter_clubs &= Q(id=user.id) @@ -177,12 +192,12 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # 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) + contains_filter("name", word, case_sensitive) + | contains_filter("user__pseudo", word, case_sensitive) & Q(user__state__in=user_state) + | contains_filter("interface__domain__name", word, case_sensitive) + | contains_filter("interface__domain__related_domain__name", word, case_sensitive) + | contains_filter("interface__mac_address", word, case_sensitive) + | contains_filter("interface__ipv4__ipv4", word, case_sensitive) ) try: _mac_addr = EUI(word, 48) @@ -195,9 +210,8 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Factures if "2" in aff: - filter_factures = Q(user__pseudo__icontains=word) & Q( - user__state__in=user_state - ) + filter_factures = contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state) if start is not None: filter_factures &= Q(date__gte=start) if end is not None: @@ -207,8 +221,9 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Bans if "3" in aff: filter_bans = ( - Q(user__pseudo__icontains=word) & Q(user__state__in=user_state) - ) | Q(raison__icontains=word) + contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state) + ) | contains_filter("raison", word, case_sensitive) if start is not None: filter_bans &= ( (Q(date_start__gte=start) & Q(date_end__gte=start)) @@ -226,8 +241,9 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Whitelists if "4" in aff: filter_whitelists = ( - Q(user__pseudo__icontains=word) & Q(user__state__in=user_state) - ) | Q(raison__icontains=word) + contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state) + ) | contains_filter("raison", word, case_sensitive) if start is not None: filter_whitelists &= ( (Q(date_start__gte=start) & Q(date_end__gte=start)) @@ -245,7 +261,10 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Rooms if "5" in aff and Room.can_view_all(user): filter_rooms = ( - Q(details__icontains=word) | Q(name__icontains=word) | Q(port__details=word) | Q(building__name__icontains=word) + contains_filter("details", word, case_sensitive) + | contains_filter("name", word, case_sensitive) + | contains_filter("building__name", word, case_sensitive) + | Q(port__details=word) ) filters["rooms"] |= filter_rooms @@ -253,12 +272,12 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Switch ports if "6" in aff and User.can_view_all(user): filter_ports = ( - Q(room__name__icontains=word) - | Q(machine_interface__domain__name__icontains=word) - | Q(related__switch__interface__domain__name__icontains=word) - | Q(custom_profile__name__icontains=word) - | Q(custom_profile__profil_default__icontains=word) - | Q(details__icontains=word) + contains_filter("room__name", word, case_sensitive) + | contains_filter("machine_interface__domain__name", word, case_sensitive) + | contains_filter("related__switch__interface__domain__name", word, case_sensitive) + | contains_filter("custom_profile__name", word, case_sensitive) + | contains_filter("custom_profile__profil_default", word, case_sensitive) + | contains_filter("details", word, case_sensitive) ) if is_int(word): filter_ports |= Q(port=word) @@ -267,13 +286,13 @@ def search_single_word(word, filters, user, start, end, user_state, aff): # Switches if "7" in aff and Switch.can_view_all(user): filter_switches = ( - Q(interface__domain__name__icontains=word) - | Q(interface__ipv4__ipv4__icontains=word) - | Q(switchbay__building__name__icontains=word) - | Q(stack__name__icontains=word) - | Q(model__reference__icontains=word) - | Q(model__constructor__name__icontains=word) - | Q(interface__details__icontains=word) + contains_filter("interface__domain__name", word, case_sensitive) + | contains_filter("interface__ipv4__ipv4", word, case_sensitive) + | contains_filter("switchbay__building__name", word, case_sensitive) + | contains_filter("stack__name", word, case_sensitive) + | contains_filter("model__reference", word, case_sensitive) + | contains_filter("model__constructor__name", word, case_sensitive) + | contains_filter("interface__details", word, case_sensitive) ) if is_int(word): filter_switches |= Q(number=word) | Q(stack_member_id=word) @@ -357,7 +376,7 @@ def search_single_query(query, filters, user, start, end, user_state, aff): return filters # Handle standard queries - return search_single_word(query.text, filters, user, start, end, user_state, aff) + return search_single_word(query.text, filters, user, start, end, user_state, aff, q.case_sensitive) def create_queries(query): @@ -400,6 +419,10 @@ def create_queries(query): if char == '"': # Toogle the keep_intact state, if true, we are between two " keep_intact = not keep_intact + + if keep_intact: + current_query.case_sensitive = True + continue if keep_intact: From 1343cd806cfc2543e3e6083389cf49bc6068a976 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 18:01:39 +0000 Subject: [PATCH 010/490] Fix identation error --- search/engine.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/search/engine.py b/search/engine.py index 7d4d8934..f8e19585 100644 --- a/search/engine.py +++ b/search/engine.py @@ -210,8 +210,10 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se # Factures if "2" in aff: - filter_factures = contains_filter("user__pseudo", word, case_sensitive) - & Q(user__state__in=user_state) + filter_factures = ( + contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state) + ) if start is not None: filter_factures &= Q(date__gte=start) if end is not None: @@ -266,7 +268,6 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se | contains_filter("building__name", word, case_sensitive) | Q(port__details=word) ) - filters["rooms"] |= filter_rooms # Switch ports @@ -376,7 +377,7 @@ def search_single_query(query, filters, user, start, end, user_state, aff): return filters # Handle standard queries - return search_single_word(query.text, filters, user, start, end, user_state, aff, q.case_sensitive) + return search_single_word(query.text, filters, user, start, end, user_state, aff, query.case_sensitive) def create_queries(query): From e2b3f792393211e6a6067f05cfd560cffe950996 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 21:45:34 +0000 Subject: [PATCH 011/490] Improve behavior when searching for rooms --- search/engine.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/search/engine.py b/search/engine.py index f8e19585..36d320e5 100644 --- a/search/engine.py +++ b/search/engine.py @@ -31,6 +31,9 @@ from __future__ import unicode_literals from netaddr import EUI, AddrFormatError from django.db.models import Q +from django.db.models import Value +from django.db.models.functions import Concat + from users.models import User, Adherent, Club, Ban, Whitelist from machines.models import Machine from topologie.models import Port, Switch, Room @@ -174,9 +177,10 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se | contains_filter("pseudo", word, case_sensitive) | contains_filter("email", word, case_sensitive) | contains_filter("telephone", word, case_sensitive) - | contains_filter("room__name", word, case_sensitive) - | contains_filter("room__building__name", word, case_sensitive) + | contains_filter("room_full_name", word, case_sensitive) # Added through annotate ) + + # Users have a name whereas clubs only have a surname filter_users = (filter_clubs | contains_filter("name", word, case_sensitive)) if not User.can_view_all(user)[0]: @@ -264,8 +268,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se if "5" in aff and Room.can_view_all(user): filter_rooms = ( contains_filter("details", word, case_sensitive) - | contains_filter("name", word, case_sensitive) - | contains_filter("building__name", word, case_sensitive) + | contains_filter("full_name", word, case_sensitive) # Added through annotate | Q(port__details=word) ) filters["rooms"] |= filter_rooms @@ -273,7 +276,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se # Switch ports if "6" in aff and User.can_view_all(user): filter_ports = ( - contains_filter("room__name", word, case_sensitive) + contains_filter("room_full_name", word, case_sensitive) # Added through annotate | contains_filter("machine_interface__domain__name", word, case_sensitive) | contains_filter("related__switch__interface__domain__name", word, case_sensitive) | contains_filter("custom_profile__name", word, case_sensitive) @@ -308,6 +311,10 @@ def apply_filters(filters, user, aff): the search query. """ # Results are later filled-in depending on the display filter + # In some cases, annotations are used to match what is displayed in the results + # For example, the displayed room is actually "room__building__name room__name" + # So queries wouldn't match what the user expects if we just kept the + # database's format results = { "users": Adherent.objects.none(), "clubs": Club.objects.none(), @@ -322,8 +329,12 @@ def apply_filters(filters, user, aff): # Users and clubs if "0" in aff: - results["users"] = Adherent.objects.filter(filters["users"]) - results["clubs"] = Club.objects.filter(filters["clubs"]) + results["users"] = Adherent.objects.annotate( + room_full_name=Concat("room__building__name", Value(" "), "room__name"), + ).filter(filters["users"]) + results["clubs"] = Club.objects.annotate( + room_full_name=Concat("room__building__name", Value(" "), "room__name"), + ).filter(filters["clubs"]) # Machines if "1" in aff: @@ -343,11 +354,15 @@ def apply_filters(filters, user, aff): # Rooms if "5" in aff and Room.can_view_all(user): - results["rooms"] = Room.objects.filter(filters["rooms"]) + results["rooms"] = Room.objects.annotate( + full_name=Concat("building__name", Value(" "), "name"), + ).filter(filters["rooms"]) # Switch ports if "6" in aff and User.can_view_all(user): - results["ports"] = Port.objects.filter(filters["ports"]) + results["ports"] = Port.objects.annotate( + room_full_name=Concat("room__building__name", Value(" "), "room__name"), + ).filter(filters["ports"]) # Switches if "7" in aff and Switch.can_view_all(user): From c110abb17fe7ce7123d99c45a4e39ab070790c22 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 23:04:38 +0100 Subject: [PATCH 012/490] Fix some translations --- search/locale/fr/LC_MESSAGES/django.po | 6 +++--- users/locale/fr/LC_MESSAGES/django.po | 2 +- users/templates/users/aff_users.html | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index efb33ebe..6f798188 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -89,10 +89,10 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" -"Use « » and «,» to specify distinct words, «\"query\"» for an exact search " -"an exact search, «\\» to escape a character and «+» to combine keywors." +'Use « » and «,» to specify distinct words, «"query"» for' +" an exact search, «\\» to escape a character and «+» to combine keywors." msgstr "" -"Utilisez « » et «,» pour spécifier différents mots, «\"mot\"» pour une " +'Utilisez « » et «,» pour spécifier différents mots, «"mot"» pour une ' "recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des mots clés." #: search/forms.py:88 diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 55780636..12580ba1 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -826,7 +826,7 @@ msgid "Shell" msgstr "Interface en ligne de commande" #: users/templates/users/aff_users.html:35 -msgid "Firt name" +msgid "First name" msgstr "Prénom" #: users/templates/users/delete.html:29 diff --git a/users/templates/users/aff_users.html b/users/templates/users/aff_users.html index 8365e259..d0704167 100644 --- a/users/templates/users/aff_users.html +++ b/users/templates/users/aff_users.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% trans "Firt name" as tr_name %} + {% trans "First name" as tr_name %} {% trans "Surname" as tr_surname %} From 38142bdc7ae4d160353c4eb76927c50e1b0c354d Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 23:37:37 +0100 Subject: [PATCH 013/490] Fix wrongfuly "No result" in search --- search/templates/search/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/templates/search/index.html b/search/templates/search/index.html index a93547cc..e560ccaa 100644 --- a/search/templates/search/index.html +++ b/search/templates/search/index.html @@ -119,7 +119,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% 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 %} + {% if not users and not clubs and not machines and not factures and not whitelists and not bans and not rooms and not ports and not switches %}

{% trans "No result" %}

{% else %}
{% blocktrans %}Only the first {{ max_result }} results are displayed in each category.{% endblocktrans %}
From 06c042cf3a4ebc9d1e17622d142da080a8535155 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 23:41:15 +0100 Subject: [PATCH 014/490] Fix typo in translation --- search/forms.py | 4 ++-- search/locale/fr/LC_MESSAGES/django.po | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/search/forms.py b/search/forms.py index 1cfd02cb..1a29ce2d 100644 --- a/search/forms.py +++ b/search/forms.py @@ -63,7 +63,7 @@ class SearchForm(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search, «\\» to escape a character and «+» to combine keywors." + " an exact search, «\\» to escape a character and «+» to combine keywords." ) ), max_length=100, @@ -78,7 +78,7 @@ class SearchFormPlus(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search, «\\» to escape a character and «+» to combine keywors." + " an exact search, «\\» to escape a character and «+» to combine keywords." ) ), max_length=100, diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 6f798188..356c1ae4 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -89,10 +89,10 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" -'Use « » and «,» to specify distinct words, «"query"» for' -" an exact search, «\\» to escape a character and «+» to combine keywors." +"Use « » and «,» to specify distinct words, «\"query\"» for" +" an exact search, «\\» to escape a character and «+» to combine keywords." msgstr "" -'Utilisez « » et «,» pour spécifier différents mots, «"mot"» pour une ' +"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour une " "recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des mots clés." #: search/forms.py:88 From 8c2df1d989bf46481a604fda32fa4ecfa827a26f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 18 Feb 2020 23:43:35 +0100 Subject: [PATCH 015/490] Remove duplicate translation --- users/locale/fr/LC_MESSAGES/django.po | 4 ---- 1 file changed, 4 deletions(-) diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 12580ba1..7d630850 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -825,10 +825,6 @@ msgstr "Group d'accès" msgid "Shell" msgstr "Interface en ligne de commande" -#: users/templates/users/aff_users.html:35 -msgid "First name" -msgstr "Prénom" - #: users/templates/users/delete.html:29 msgid "Deletion of users" msgstr "Suppression d'utilisateurs" From 9cc017a0eaafec994337ba85eff5c8960649c353 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 19 Feb 2020 09:50:16 +0000 Subject: [PATCH 016/490] Add ability to search by building and room name without space --- search/engine.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/search/engine.py b/search/engine.py index 36d320e5..62ca5e53 100644 --- a/search/engine.py +++ b/search/engine.py @@ -178,6 +178,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se | contains_filter("email", word, case_sensitive) | contains_filter("telephone", word, case_sensitive) | contains_filter("room_full_name", word, case_sensitive) # Added through annotate + | contains_filter("room_full_name_stuck", word, case_sensitive) # Added through annotate ) # Users have a name whereas clubs only have a surname @@ -269,6 +270,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se filter_rooms = ( contains_filter("details", word, case_sensitive) | contains_filter("full_name", word, case_sensitive) # Added through annotate + | contains_filter("full_name_stuck", word, case_sensitive) # Added through annotate | Q(port__details=word) ) filters["rooms"] |= filter_rooms @@ -277,6 +279,7 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se if "6" in aff and User.can_view_all(user): filter_ports = ( contains_filter("room_full_name", word, case_sensitive) # Added through annotate + | contains_filter("room_full_name_stuck", word, case_sensitive) # Added through annotate | contains_filter("machine_interface__domain__name", word, case_sensitive) | contains_filter("related__switch__interface__domain__name", word, case_sensitive) | contains_filter("custom_profile__name", word, case_sensitive) @@ -331,9 +334,11 @@ def apply_filters(filters, user, aff): if "0" in aff: results["users"] = Adherent.objects.annotate( room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["users"]) results["clubs"] = Club.objects.annotate( room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["clubs"]) # Machines @@ -356,12 +361,14 @@ def apply_filters(filters, user, aff): if "5" in aff and Room.can_view_all(user): results["rooms"] = Room.objects.annotate( full_name=Concat("building__name", Value(" "), "name"), + full_name_stuck=Concat("building__name", "name"), ).filter(filters["rooms"]) # Switch ports if "6" in aff and User.can_view_all(user): results["ports"] = Port.objects.annotate( room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["ports"]) # Switches From 9f74655cbd39b9f3419359810c4e9f1d85c6f494 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 19 Feb 2020 11:06:57 +0100 Subject: [PATCH 017/490] Fix linting issues --- search/engine.py | 71 +++++++++++++++++--------- search/forms.py | 6 ++- search/locale/fr/LC_MESSAGES/django.po | 10 ++-- search/views.py | 18 +++++-- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/search/engine.py b/search/engine.py index 62ca5e53..3dfd0680 100644 --- a/search/engine.py +++ b/search/engine.py @@ -144,7 +144,10 @@ def finish_results(request, results, col, order): max_result = GeneralOption.get_cached_value("search_display_page") for name, val in results.items(): page_arg = name + "_page" - results[name] = re2o_paginator(request, val.distinct(), max_result, page_arg=page_arg) + results[name] = re2o_paginator(request, + val.distinct(), + max_result, + page_arg=page_arg) results.update({"max_result": max_result}) @@ -162,7 +165,8 @@ def contains_filter(attribute, word, case_sensitive=False): return Q(**{attr: word}) -def search_single_word(word, filters, user, start, end, user_state, aff, case_sensitive=False): +def search_single_word(word, filters, user, start, end, + user_state, aff, case_sensitive=False): """ 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 @@ -177,12 +181,16 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se | contains_filter("pseudo", word, case_sensitive) | contains_filter("email", word, case_sensitive) | contains_filter("telephone", word, case_sensitive) - | contains_filter("room_full_name", word, case_sensitive) # Added through annotate - | contains_filter("room_full_name_stuck", word, case_sensitive) # Added through annotate + # Added through annotate + | contains_filter("room_full_name", word, case_sensitive) + | contains_filter("room_full_name_stuck", word, case_sensitive) ) # Users have a name whereas clubs only have a surname - filter_users = (filter_clubs | contains_filter("name", word, case_sensitive)) + filter_users = ( + filter_clubs + | contains_filter("name", word, case_sensitive) + ) if not User.can_view_all(user)[0]: filter_clubs &= Q(id=user.id) @@ -198,14 +206,16 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se if "1" in aff: filter_machines = ( contains_filter("name", word, case_sensitive) - | contains_filter("user__pseudo", word, case_sensitive) & Q(user__state__in=user_state) + | (contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state)) | contains_filter("interface__domain__name", word, case_sensitive) - | contains_filter("interface__domain__related_domain__name", word, case_sensitive) + | contains_filter("interface__domain__related_domain__name", + word, case_sensitive) | contains_filter("interface__mac_address", word, case_sensitive) | contains_filter("interface__ipv4__ipv4", word, case_sensitive) ) try: - _mac_addr = EUI(word, 48) + _ = EUI(word, 48) filter_machines |= Q(interface__mac_address=word) except AddrFormatError: pass @@ -269,8 +279,9 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se if "5" in aff and Room.can_view_all(user): filter_rooms = ( contains_filter("details", word, case_sensitive) - | contains_filter("full_name", word, case_sensitive) # Added through annotate - | contains_filter("full_name_stuck", word, case_sensitive) # Added through annotate + # Added through annotate + | contains_filter("full_name", word, case_sensitive) + | contains_filter("full_name_stuck", word, case_sensitive) | Q(port__details=word) ) filters["rooms"] |= filter_rooms @@ -278,13 +289,17 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se # Switch ports if "6" in aff and User.can_view_all(user): filter_ports = ( - contains_filter("room_full_name", word, case_sensitive) # Added through annotate - | contains_filter("room_full_name_stuck", word, case_sensitive) # Added through annotate - | contains_filter("machine_interface__domain__name", word, case_sensitive) - | contains_filter("related__switch__interface__domain__name", word, case_sensitive) + contains_filter("machine_interface__domain__name", + word, case_sensitive) + | contains_filter("related__switch__interface__domain__name", + word, case_sensitive) | contains_filter("custom_profile__name", word, case_sensitive) - | contains_filter("custom_profile__profil_default", word, case_sensitive) + | contains_filter("custom_profile__profil_default", + word, case_sensitive) | contains_filter("details", word, case_sensitive) + # Added through annotate + | contains_filter("room_full_name", word, case_sensitive) + | contains_filter("room_full_name_stuck", word, case_sensitive) ) if is_int(word): filter_ports |= Q(port=word) @@ -295,7 +310,8 @@ def search_single_word(word, filters, user, start, end, user_state, aff, case_se filter_switches = ( contains_filter("interface__domain__name", word, case_sensitive) | contains_filter("interface__ipv4__ipv4", word, case_sensitive) - | contains_filter("switchbay__building__name", word, case_sensitive) + | contains_filter("switchbay__building__name", + word, case_sensitive) | contains_filter("stack__name", word, case_sensitive) | contains_filter("model__reference", word, case_sensitive) | contains_filter("model__constructor__name", word, case_sensitive) @@ -314,10 +330,10 @@ def apply_filters(filters, user, aff): the search query. """ # Results are later filled-in depending on the display filter - # In some cases, annotations are used to match what is displayed in the results - # For example, the displayed room is actually "room__building__name room__name" - # So queries wouldn't match what the user expects if we just kept the - # database's format + # In some cases, annotations are used to match what is displayed in the + # results. For example, the displayed room is actually + # "room__building__name room__name", so queries wouldn't match what the + # user expects if we just kept the database's format results = { "users": Adherent.objects.none(), "clubs": Club.objects.none(), @@ -333,11 +349,13 @@ def apply_filters(filters, user, aff): # Users and clubs if "0" in aff: results["users"] = Adherent.objects.annotate( - room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name=Concat("room__building__name", + Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["users"]) results["clubs"] = Club.objects.annotate( - room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name=Concat("room__building__name", + Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["clubs"]) @@ -367,7 +385,8 @@ def apply_filters(filters, user, aff): # Switch ports if "6" in aff and User.can_view_all(user): results["ports"] = Port.objects.annotate( - room_full_name=Concat("room__building__name", Value(" "), "room__name"), + room_full_name=Concat("room__building__name", + Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["ports"]) @@ -386,7 +405,8 @@ def search_single_query(query, filters, user, start, end, user_state, aff): newfilters = empty_filters() for q in query.subqueries: # Construct an independent filter for each subquery - subfilters = search_single_query(q, empty_filters(), user, start, end, user_state, aff) + subfilters = search_single_query(q, empty_filters(), user, + start, end, user_state, aff) # Apply the subfilter for field in filter_fields(): @@ -399,7 +419,8 @@ def search_single_query(query, filters, user, start, end, user_state, aff): return filters # Handle standard queries - return search_single_word(query.text, filters, user, start, end, user_state, aff, query.case_sensitive) + return search_single_word(query.text, filters, user, start, end, + user_state, aff, query.case_sensitive) def create_queries(query): diff --git a/search/forms.py b/search/forms.py index 1a29ce2d..f6e90cd7 100644 --- a/search/forms.py +++ b/search/forms.py @@ -63,7 +63,8 @@ class SearchForm(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search, «\\» to escape a character and «+» to combine keywords." + " search, «\\» to escape a character and «+» to" + " combine keywords." ) ), max_length=100, @@ -78,7 +79,8 @@ class SearchFormPlus(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " an exact search, «\\» to escape a character and «+» to combine keywords." + " an exact search, «\\» to escape a character and «+» to" + " combine keywords." ) ), max_length=100, diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 356c1ae4..5afe48c4 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -89,11 +89,13 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" -"Use « » and «,» to specify distinct words, «\"query\"» for" -" an exact search, «\\» to escape a character and «+» to combine keywords." +"Use « » and «,» to specify distinct words, «"query"» for" +" search, «\\» to escape a character and «+» to" +" combine keywords." msgstr "" -"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour une " -"recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des mots clés." +"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour" +" une recherche exacte, «\\» pour échapper un caractère et «+» pour" +" combiner des mots clés." #: search/forms.py:88 msgid "Users filter" diff --git a/search/views.py b/search/views.py index 60d0a92f..a994240f 100644 --- a/search/views.py +++ b/search/views.py @@ -43,7 +43,8 @@ from search.forms import ( ) from re2o.acl import can_view_all -from .engine import * +from .engine import empty_filters, create_queries, search_single_query +from .engine import apply_filters, finish_results def get_results(query, request, params): @@ -66,7 +67,12 @@ def get_results(query, request, params): ) results = apply_filters(filters, request.user, aff) - results = finish_results(request, results, request.GET.get("col"), request.GET.get("order")) + results = finish_results( + request, + results, + request.GET.get("col"), + request.GET.get("order") + ) results.update({"search_term": query}) return results @@ -82,7 +88,9 @@ def search(request): request, "search/index.html", get_results( - search_form.cleaned_data.get("q", ""), request, search_form.cleaned_data + search_form.cleaned_data.get("q", ""), + request, + search_form.cleaned_data ), ) return render(request, "search/search.html", {"search_form": search_form}) @@ -98,7 +106,9 @@ def searchp(request): request, "search/index.html", get_results( - search_form.cleaned_data.get("q", ""), request, search_form.cleaned_data + search_form.cleaned_data.get("q", ""), + request, + search_form.cleaned_data ), ) return render(request, "search/search.html", {"search_form": search_form}) From 73e7095cc45875d98033f7d6dbe9faba749f4479 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 19 Feb 2020 11:07:53 +0100 Subject: [PATCH 018/490] Fix translation --- search/locale/fr/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 5afe48c4..749f4184 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -89,7 +89,7 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" -"Use « » and «,» to specify distinct words, «"query"» for" +"Use « » and «,» to specify distinct words, «\"query\"» for" " search, «\\» to escape a character and «+» to" " combine keywords." msgstr "" From 010169aab1ecada9066272d66b7bc601a19186af Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 19 Feb 2020 11:10:07 +0100 Subject: [PATCH 019/490] Fix wrong translation in advanced search --- search/forms.py | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/search/forms.py b/search/forms.py index f6e90cd7..9f2ff82a 100644 --- a/search/forms.py +++ b/search/forms.py @@ -63,7 +63,7 @@ class SearchForm(Form): help_text=( _( 'Use « » and «,» to specify distinct words, «"query"» for' - " search, «\\» to escape a character and «+» to" + " an exact search, «\\» to escape a character and «+» to" " combine keywords." ) ), diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 749f4184..ca0507ae 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -90,7 +90,7 @@ msgstr "Rechercher" #: search/forms.py:65 search/forms.py:80 msgid "" "Use « » and «,» to specify distinct words, «\"query\"» for" -" search, «\\» to escape a character and «+» to" +" an exact search, «\\» to escape a character and «+» to" " combine keywords." msgstr "" "Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour" From 6a14f72a163e253098a8474d74e982b1a40af5a1 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 20 Feb 2020 08:07:41 +0000 Subject: [PATCH 020/490] Fix permission allowing user to change groups --- users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/models.py b/users/models.py index 3d21548a..75a38aa9 100755 --- a/users/models.py +++ b/users/models.py @@ -1090,7 +1090,7 @@ class User( :returns: a message and a boolean which is True if the user has the right to change a group """ - can = user_request.has_perm("users.change_user_grou") + can = user_request.has_perm("users.change_user_groups") return ( can, _("You don't have the right to edit the user's groups of rights.") From 834853b0ca77945fa726d0368beafb4b9b9c99fe Mon Sep 17 00:00:00 2001 From: grizzly Date: Fri, 28 Feb 2020 20:24:10 +0100 Subject: [PATCH 021/490] Adding english version, respond to #157 --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index f4e38aa9..c62de6ad 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**Note:** English version below. + # Re2o GNU public license v2.0 @@ -73,3 +75,34 @@ Il est ensuite aisé de faire des requêtes, par exemple Des exemples et la documentation complète sur les requêtes django sont disponible sur le site officiel. + +---- + +# Re2o + +GNU Public license v2.0 + +## Foreword + +Re2o is a management software initialy developed at [rezometz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup. + +Re2o is based on the Django framework and Python3. It's core functionalities includes managing the members, their machines, their invoices and their access to the network but also the topology of the network and its devices. +On top of this, it is possible to plug services to enhance the possibilities and fit the need of each organization. + +# Setup + +A tutorial is available on the [Wiki](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Quick%20Start) to describe the setup process. + +# General Functioning + +Re2o follow the general functioning of a Django project and split its components between the models (describe the database objects), the templates (that define the front end), the views (that populate and serve the templates) and the forms (that provide front end object edition/creation/removal). This framework provide an abstraction layer to manipulate SQL objects as Python objects. + +Functionalities are grouped in apps (users, machines, topologie,...). Along the core functionalities, optional functionalities are available and can be activated in the preferences. + +## Rest API + +Re2o provide a Rest API to allow external services (dhcp, dns, firewall,...) installed on remote machines to access database informations in Json format. Those services are optional and should be installed and activated to fit each organization needs. + +# Wiki + +The [Wiki](https://gitlab.federez.net/federez/re2o/-/wikis/home) is available to provide information and instruction for most components of Re2o. From 1b415b99c638fb09a4b8ec78566f96e3e88d90fb Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 1 Mar 2020 00:07:10 +0000 Subject: [PATCH 022/490] finish readme --- README.md | 70 +++++++++++-------------------------------------------- 1 file changed, 13 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index c62de6ad..e5ca4cdf 100644 --- a/README.md +++ b/README.md @@ -6,75 +6,31 @@ GNU public license v2.0 ## Avant propos -Re2o est un logiciel d'administration développé initialement au rezometz. Il -se veut agnostique au réseau considéré, de manière à être installable en -quelques clics. +Re2o est un logiciel d'administration développé initialement au [rezometz](https://www.rezometz.org/). Il +se veut agnostique au réseau considéré, de manière à être installable et configurable facilement. Il utilise le framework django avec python3. Il permet de gérer les adhérents, les machines, les factures, les droits d'accès, les switchs et la topologie du réseau. -De cette manière, il est possible de pluguer très facilement des services -dessus, qui accèdent à la base de donnée en passant par django (ex : dhcp), en -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). +Il est possible d'activer très facilement des services qui améliorerons les possibilités de Re2o pour convenir au mieux aux besoins de chaque association. -# Installation +## Installation Un tutoriel pour installer le projet est disponible [sur le wiki](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Quick%20Start). -# Installations Optionnelles -## Générer le schéma des dépendances +## Fonctionnement Général -Pour cela : - * apt install python3-django-extensions - * python3 manage.py graph_models -a -g -o re2o.png +Re2o utilise le Framework Django et suit donc le principe de toutes les applications Django. Les différents composants sont les models (qui définissent les entrées de la base de données), les templates (qui définissent les pages), les views (qui génèrent les templates avec les données pertinentes), et les forms (qui définissent les pages de modification des objets). Ce framework permet de manipuler les données comme des objets Python. -# Fonctionnement interne +Tous ces composants sont regroupés en apps (users, machines, topologie,...). Certaines de ces apps constituent le coeur de Re2o et sont indispensables à son fonctionnement. Certaines autres apps sont optionnelles et peuvent être activées en fonction des besoins de chaque association. -## Fonctionnement général +## API Rest -Re2o est séparé entre les models, qui sont visibles sur le schéma des -dépendances. Il s'agit en réalité des tables sql, et les fields étant les -colonnes. -Ceci dit il n'est jamais nécessaire de toucher directement au sql, django -procédant automatiquement à tout cela. -On crée donc différents models (user, right pour les droits des users, -interfaces, IpList pour l'ensemble des adresses ip, etc) +Les données stockées dans Re2o sont disponibles via un API Rest. Les services installés sur d'autres machines (dhcp, dns, firewall,...) utilisent cet API pour avoir accès aux données des utilisateurs et fonctionner. -Du coté des forms, il s'agit des formulaires d'édition des models. Il -s'agit de ModelForms django, qui héritent des models très simplement, voir la -documentation django models forms. +# Wiki -Enfin les views, générent les pages web à partir des forms et des templates. - -## Fonctionnement avec les services - -Les services dhcp.py, dns.py etc accèdent aux données via des vues rest. -Celles-ci se trouvent dans machines/views.py. Elles sont générées via -machines/serializers.py qui génère les vues. IL s'agit de vues en json utilisées -par re2o-tools pour récupérer les données. -Il est nécessaire de créer un user dans re2o avec le droit serveur qui permet -d'accéder à ces vues, utilisé par re2o-tools. - -# Requète en base de donnée - -Pour avoir un shell, lancer : -```.bash -python3 manage.py shell -``` - -Pour charger des objets (exemple avec User), faire : -```.python -from users.models import User -``` - -Pour charger les objets django, il suffit de faire `User.objects.all()` -pour tous les users par exemple. -Il est ensuite aisé de faire des requêtes, par exemple -`User.objects.filter(pseudo='test')` - -Des exemples et la documentation complète sur les requêtes django sont -disponible sur le site officiel. +Le [Wiki](https://gitlab.federez.net/federez/re2o/-/wikis/home) est accessible sur le gitlab de Federez. Il regroupe les informations et instructions pour la plupart des composants de Re2o. ---- @@ -84,9 +40,9 @@ GNU Public license v2.0 ## Foreword -Re2o is a management software initialy developed at [rezometz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup. +Re2o is a management software initially developed at [rezometz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup. -Re2o is based on the Django framework and Python3. It's core functionalities includes managing the members, their machines, their invoices and their access to the network but also the topology of the network and its devices. +Re2o is based on the Django framework and Python3. Its core functionalities include managing the members, their machines, their invoices and their rights to the network but also the topology of the network and its devices. On top of this, it is possible to plug services to enhance the possibilities and fit the need of each organization. # Setup From 15cec0c7f304f947f0ed954f66a68eaecc3e2281 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Sat, 25 Jan 2020 23:37:17 +0100 Subject: [PATCH 023/490] Add group mailings --- api/views.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/api/views.py b/api/views.py index b53ee974..4077eeeb 100644 --- a/api/views.py +++ b/api/views.py @@ -30,6 +30,7 @@ import datetime from django.conf import settings from django.db.models import Q +from django.contrib.auth.models import Group from rest_framework import viewsets, generics, views from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import ObtainAuthToken @@ -759,7 +760,15 @@ class StandardMailingView(views.APIView): adherents_data = serializers.MailingMemberSerializer( all_has_access(), many=True ).data + data = [{"name": "adherents", "members": adherents_data}] + groups = Group.objects.all() + for group in groups: + group_data = serializers.MailingMemberSerializer( + group.user_set.all(), many=True + ).data + data.append({"name": group.name, "members": group_data}) + paginator = self.pagination_class() paginator.paginate_queryset(data, request) return paginator.get_paginated_response(data) From f02740189a6d03ce9148228669e76d80efe75d26 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Fri, 3 Apr 2020 17:01:38 +0200 Subject: [PATCH 024/490] Fix TTL export in API. --- api/serializers.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index a2722d2a..6a40d525 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -342,7 +342,7 @@ class DomainSerializer(NamespacedHMSerializer): class Meta: model = machines.Domain - fields = ("interface_parent", "name", "extension", "cname", "api_url") + fields = ("interface_parent", "name", "extension", "cname", "api_url", "ttl") class IpListSerializer(NamespacedHMSerializer): @@ -473,6 +473,7 @@ class OptionalMachineSerializer(NamespacedHMSerializer): "ipv6_mode", "create_machine", "ipv6", + "default_dns_ttl" ) @@ -1206,7 +1207,7 @@ class NSRecordSerializer(NsSerializer): target = serializers.CharField(source="ns", read_only=True) class Meta(NsSerializer.Meta): - fields = ("target",) + fields = ("target", "ttl") class MXRecordSerializer(MxSerializer): @@ -1217,7 +1218,7 @@ class MXRecordSerializer(MxSerializer): target = serializers.CharField(source="name", read_only=True) class Meta(MxSerializer.Meta): - fields = ("target", "priority") + fields = ("target", "priority", "ttl") class TXTRecordSerializer(TxtSerializer): @@ -1226,7 +1227,7 @@ class TXTRecordSerializer(TxtSerializer): """ class Meta(TxtSerializer.Meta): - fields = ("field1", "field2") + fields = ("field1", "field2", "ttl") class SRVRecordSerializer(SrvSerializer): @@ -1269,10 +1270,11 @@ class ARecordSerializer(serializers.ModelSerializer): hostname = serializers.CharField(source="domain.name", read_only=True) ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) + ttl = serializers.IntegerField(source="domain.ttl", read_only=True) class Meta: model = machines.Interface - fields = ("hostname", "ipv4") + fields = ("hostname", "ipv4", "ttl") class AAAARecordSerializer(serializers.ModelSerializer): @@ -1282,10 +1284,11 @@ class AAAARecordSerializer(serializers.ModelSerializer): hostname = serializers.CharField(source="domain.name", read_only=True) ipv6 = Ipv6ListSerializer(many=True, read_only=True) + ttl = serializers.IntegerField(source="domain.ttl", read_only=True) class Meta: model = machines.Interface - fields = ("hostname", "ipv6") + fields = ("hostname", "ipv6", "ttl") class CNAMERecordSerializer(serializers.ModelSerializer): @@ -1298,7 +1301,7 @@ class CNAMERecordSerializer(serializers.ModelSerializer): class Meta: model = machines.Domain - fields = ("alias", "hostname") + fields = ("alias", "hostname", "ttl") class DNAMERecordSerializer(serializers.ModelSerializer): @@ -1311,7 +1314,7 @@ class DNAMERecordSerializer(serializers.ModelSerializer): class Meta: model = machines.DName - fields = ("alias", "zone") + fields = ("alias", "zone", "ttl") class DNSZonesSerializer(serializers.ModelSerializer): From 07acce2e90e4bef523a8849dbb3024b7346d9baf Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 16:58:20 +0000 Subject: [PATCH 025/490] Add optional fields to select password during user creation --- static/js/toggle_password_fields.js | 24 +++++++++++ users/forms.py | 64 ++++++++++++++++++++++++++++- users/views.py | 40 +++++++++++++++--- 3 files changed, 121 insertions(+), 7 deletions(-) create mode 100644 static/js/toggle_password_fields.js diff --git a/static/js/toggle_password_fields.js b/static/js/toggle_password_fields.js new file mode 100644 index 00000000..3c1f436f --- /dev/null +++ b/static/js/toggle_password_fields.js @@ -0,0 +1,24 @@ +/** This makes an checkbox toggle the appeareance of the + * password and password confirmations fields. + */ +function toggle_show_password_chkbox() { + var password1 = document.getElementById('id_Adherent-password1'); + var password2 = document.getElementById('id_Adherent-password2'); + + if (show_password_chkbox.checked) { + password1.parentElement.style.display = 'none'; + password2.parentElement.style.display = 'none'; + password1.required = false; + password2.required = false; + } else { + password1.parentElement.style.display = 'block'; + password2.parentElement.style.display = 'block'; + password1.required = true; + password2.required = true; + } +} + +var show_password_chkbox = document.getElementById('id_Adherent-init_password_by_mail'); +show_password_chkbox.onclick = toggle_show_password_chkbox; +toggle_show_password_chkbox(); + diff --git a/users/forms.py b/users/forms.py index cef1e43a..c5200fc2 100644 --- a/users/forms.py +++ b/users/forms.py @@ -380,7 +380,28 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. AdherentForm auquel on ajoute une checkbox afin d'éviter les - doublons d'utilisateurs""" + doublons d'utilisateurs et, optionnellement, + un champ mot de passe""" + # Champ pour choisir si un lien est envoyé par mail pour le mot de passe + init_password_by_mail = forms.BooleanField(required=False, initial=True) + init_password_by_mail.label = _("Send password reset link by email.") + + # Champs pour initialiser le mot de passe + # Validators are handled manually since theses fields aren't always required + password1 = forms.CharField( + required=False, + label=_("Password"), + widget=forms.PasswordInput, + # validators=[MinLengthValidator(8)], + max_length=255, + ) + password2 = forms.CharField( + required=False, + label=_("Password confirmation"), + widget=forms.PasswordInput, + # validators=[MinLengthValidator(8)], + max_length=255, + ) # Champ permettant d'éviter au maxium les doublons d'utilisateurs former_user_check_info = _( @@ -422,6 +443,47 @@ class AdherentCreationForm(AdherentForm): ) ) + def clean_password1(self): + """Ignore ce champs si la case init_password_by_mail est décochée""" + send_email = self.cleaned_data.get("init_password_by_mail") + if send_email: + return None + + password1 = self.cleaned_data.get("password1") + if len(password1) < 8: + raise forms.ValidationError(_("Password must contain at least 8 characters.")) + + return password1 + + def clean_password2(self): + """Verifie que password1 et 2 sont identiques (si nécessaire)""" + send_email = self.cleaned_data.get("init_password_by_mail") + if send_email: + return None + + # Check that the two password entries match + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") + if password1 and password2 and password1 != password2: + raise forms.ValidationError(_("The passwords don't match.")) + + return password2 + + def save(self, commit=True): + """Set the user's password, if entered + Returns the user and a bool indicating whether + an email to init the password should be sent""" + # Save the provided password in hashed format + user = super(AdherentForm, self).save(commit=False) + + send_email = self.cleaned_data.get("init_password_by_mail") + if not send_email: + user.set_password(self.cleaned_data["password1"]) + + user.should_send_password_reset_email = send_email + user.save() + return user + class AdherentEditForm(AdherentForm): """Formulaire d'édition d'un user. diff --git a/users/views.py b/users/views.py index 3fb4d472..f14cb295 100644 --- a/users/views.py +++ b/users/views.py @@ -119,15 +119,42 @@ def new_user(request): user = AdherentCreationForm(request.POST or None, user=request.user) GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") GTU = GeneralOption.get_cached_value("GTU") + if user.is_valid(): user = user.save() - user.reset_passwd_mail(request) - messages.success( - request, - _("The user %s was created, an email to set the password was sent.") - % user.pseudo, - ) + + # Use "is False" so that if None, the email is sent + if user.should_send_password_reset_email is False: + messages.success( + request, + _("The user %s was created.") + % user.pseudo, + ) + else: + user.reset_passwd_mail(request) + messages.success( + request, + _("The user %s was created, an email to set the password was sent.") + % user.pseudo, + ) + return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) + + # Anonymous users are allowed to create new accounts + # but they should be treated differently + params = { + "userform": user, + "GTU_sum_up": GTU_sum_up, + "GTU": GTU, + "showCGU": True, + "action_name": _("Commit"), + } + + if request.user.is_anonymous: + params["load_js_file"] = "/static/js/toggle_password_fields.js" + + return form(params, "users/user.html", request) + """ return form( { "userform": user, @@ -139,6 +166,7 @@ def new_user(request): "users/user.html", request, ) + """ @login_required From 86d9db350a511fd345046bb3ecbcc3fd82c1c441 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 17:16:33 +0000 Subject: [PATCH 026/490] Add option to enable the password field during account creation --- ...allow_set_password_during_user_creation.py | 20 +++++++++ preferences/models.py | 9 ++++ .../preferences/display_preferences.html | 4 ++ users/forms.py | 42 ++++++++++--------- users/views.py | 30 ++++--------- 5 files changed, 64 insertions(+), 41 deletions(-) create mode 100644 preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py diff --git a/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py b/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py new file mode 100644 index 00000000..63d9e4c9 --- /dev/null +++ b/preferences/migrations/0068_optionaluser_allow_set_password_during_user_creation.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-16 17:06 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0067_auto_20191120_0159'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='allow_set_password_during_user_creation', + field=models.BooleanField(default=False, help_text='If True, users have the choice to receive an email containing a link to reset their password during creation, or to directly set their password in the page. If False, an email is always sent.'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index b8189384..8570e79b 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -117,6 +117,15 @@ class OptionalUser(AclMixin, PreferencesModel): " If False, only when a valid registration has been paid." ), ) + allow_set_password_during_user_creation = models.BooleanField( + default=False, + help_text=_( + "If True, users have the choice to receive an email containing" + " a link to reset their password during creation, or to directly" + " set their password in the page." + " If False, an email is always sent." + ), + ) allow_archived_connexion = models.BooleanField( default=False, help_text=_("If True, archived users are allowed to connect.") ) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 8e00962e..89858ddc 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -125,6 +125,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+ + + + diff --git a/users/forms.py b/users/forms.py index c5200fc2..7b133c4b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -382,26 +382,27 @@ class AdherentCreationForm(AdherentForm): AdherentForm auquel on ajoute une checkbox afin d'éviter les doublons d'utilisateurs et, optionnellement, un champ mot de passe""" - # Champ pour choisir si un lien est envoyé par mail pour le mot de passe - init_password_by_mail = forms.BooleanField(required=False, initial=True) - init_password_by_mail.label = _("Send password reset link by email.") + if OptionalUser.get_cached_value("allow_set_password_during_user_creation"): + # Champ pour choisir si un lien est envoyé par mail pour le mot de passe + init_password_by_mail = forms.BooleanField(required=False, initial=True) + init_password_by_mail.label = _("Send password reset link by email.") - # Champs pour initialiser le mot de passe - # Validators are handled manually since theses fields aren't always required - password1 = forms.CharField( - required=False, - label=_("Password"), - widget=forms.PasswordInput, - # validators=[MinLengthValidator(8)], - max_length=255, - ) - password2 = forms.CharField( - required=False, - label=_("Password confirmation"), - widget=forms.PasswordInput, - # validators=[MinLengthValidator(8)], - max_length=255, - ) + # Champs pour initialiser le mot de passe + # Validators are handled manually since theses fields aren't always required + password1 = forms.CharField( + required=False, + label=_("Password"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) + password2 = forms.CharField( + required=False, + label=_("Password confirmation"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) # Champ permettant d'éviter au maxium les doublons d'utilisateurs former_user_check_info = _( @@ -476,7 +477,8 @@ class AdherentCreationForm(AdherentForm): # Save the provided password in hashed format user = super(AdherentForm, self).save(commit=False) - send_email = self.cleaned_data.get("init_password_by_mail") + is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") + send_email = not is_set_password_allowed or self.cleaned_data.get("init_password_by_mail") if not send_email: user.set_password(self.cleaned_data["password1"]) diff --git a/users/views.py b/users/views.py index f14cb295..3f41a990 100644 --- a/users/views.py +++ b/users/views.py @@ -119,12 +119,13 @@ def new_user(request): user = AdherentCreationForm(request.POST or None, user=request.user) GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") GTU = GeneralOption.get_cached_value("GTU") + is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") if user.is_valid(): user = user.save() # Use "is False" so that if None, the email is sent - if user.should_send_password_reset_email is False: + if is_set_password_allowed and user.should_send_password_reset_email is False: messages.success( request, _("The user %s was created.") @@ -143,30 +144,17 @@ def new_user(request): # Anonymous users are allowed to create new accounts # but they should be treated differently params = { - "userform": user, - "GTU_sum_up": GTU_sum_up, - "GTU": GTU, - "showCGU": True, - "action_name": _("Commit"), - } + "userform": user, + "GTU_sum_up": GTU_sum_up, + "GTU": GTU, + "showCGU": True, + "action_name": _("Commit"), + } - if request.user.is_anonymous: + if is_set_password_allowed: params["load_js_file"] = "/static/js/toggle_password_fields.js" return form(params, "users/user.html", request) - """ - return form( - { - "userform": user, - "GTU_sum_up": GTU_sum_up, - "GTU": GTU, - "showCGU": True, - "action_name": _("Commit"), - }, - "users/user.html", - request, - ) - """ @login_required From b947573d8fad6aaa0c957e6febb846b786b57502 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 17:39:28 +0000 Subject: [PATCH 027/490] Add translations for initial password related strings --- preferences/forms.py | 1 + preferences/locale/fr/LC_MESSAGES/django.po | 22 ++++++++++++++++- users/locale/fr/LC_MESSAGES/django.po | 27 ++++++++++++++++++++- 3 files changed, 48 insertions(+), 2 deletions(-) diff --git a/preferences/forms.py b/preferences/forms.py index 9ea9fca5..2591b73b 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -68,6 +68,7 @@ class EditOptionalUserForm(ModelForm): self.fields["all_can_create_adherent"].label = _("All can create a member") self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") + self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") class EditOptionalMachineForm(ModelForm): diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 0047c101..deb77b22 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -56,6 +56,11 @@ msgstr "Tous peuvent créer un adhérent" msgid "Self registration" msgstr "Autoinscription" +#: preferences/forms.py:70 +#: preferences/templates/preferences/display_preferences.html:120 +msgid "Allow directly setting a password during account creation" +msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" + #: preferences/forms.py:70 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" @@ -292,6 +297,18 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." +#: preferences/models.py:116 +msgid "" +"If True, users have the choice to receive an email containing" +" a link to reset their password during creation, or to directly" +" set their password in the page." +" If False, an email is always sent." +msgstr "" +"Si True, les utilisateurs ont le choix de recevoir un email avec" +" un lien pour changer leur mot de passe lors de la création de leur compte, " +" ou alors de choisir leur mot de passe immédiatement." +" Si False, un email est toujours envoyé." + #: preferences/models.py:121 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." @@ -1049,7 +1066,10 @@ msgstr "%(delete_notyetactive)s jours" msgid "All users are active by default" msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/templates/preferences/display_preferences.html:128 +msgid "Allow directly entering a password during account creation" +msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" + +#: preferences/templates/preferences/display_preferences.html:130 msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 7d630850..4d8794b6 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -157,7 +157,23 @@ msgstr "Vous ne pouvez pas utiliser une adresse {}." msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:387 +#: users/forms.py:404 +msgid "" +"If this options is set, you will receive a link to set" +" your initial password by email. If you do not have" +" any means of accessing your emails, you can disable" +" this option to set your password immediatly." +msgstr "" +"Si cette option est activée, vous recevrez un lien pour choisir" +" votre mot de passe par email. Si vous n'avez pas accès" +" à vos mails, vous pouvez désactiver cette option" +" pour immédiatement entrer votre mot de passe." + +#: users/forms.py:442 +msgid "Send password reset link by email." +msgstr "Envoyer le lien de modification du mot de passe par mail." + +#: users/forms.py:435 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -188,6 +204,10 @@ msgstr "Laissez vide si vous n'avez pas de clé GPG." msgid "Default shell" msgstr "Interface en ligne de commande par défaut" +#: users/forms.py:166 users/forms.py:481 +msgid "Password must contain at least 8 characters." +msgstr "Le mot de passe doit contenir au moins 8 caractères." + #: users/forms.py:463 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" @@ -1242,6 +1262,11 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" +#: users/views.py:131 +#, python-format +msgid "The user %s was created." +msgstr "L'utilisateur %s a été créé." + #: users/views.py:127 #, python-format msgid "The user %s was created, an email to set the password was sent." From 19261400d1f33db4b1c2afb6f283154230f676db Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 17:59:53 +0000 Subject: [PATCH 028/490] Add help text for password checkbox in user creation --- users/forms.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/users/forms.py b/users/forms.py index 7b133c4b..3cac1d16 100644 --- a/users/forms.py +++ b/users/forms.py @@ -384,7 +384,18 @@ class AdherentCreationForm(AdherentForm): un champ mot de passe""" if OptionalUser.get_cached_value("allow_set_password_during_user_creation"): # Champ pour choisir si un lien est envoyé par mail pour le mot de passe - init_password_by_mail = forms.BooleanField(required=False, initial=True) + init_password_by_mail_info = _( + "If this options is set, you will receive a link to set" + " your initial password by email. If you do not have" + " any means of accessing your emails, you can disable" + " this option to set your password immediatly." + ) + + init_password_by_mail = forms.BooleanField( + help_text=init_password_by_mail_info, + required=False, + initial=True + ) init_password_by_mail.label = _("Send password reset link by email.") # Champs pour initialiser le mot de passe From 64626335d2b144846f0350c07b71fc5bd36cd697 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 22:06:14 +0200 Subject: [PATCH 029/490] Create EMAIL_NOT_YET_CONFIRMED state --- freeradius_utils/auth.py | 2 +- logs/views.py | 10 +++++ re2o/utils.py | 2 +- users/forms.py | 14 +++++++ users/management/commands/archive.py | 1 + users/models.py | 40 ++++++++++++++++++- .../users/email_confirmation_request | 33 +++++++++++++++ users/views.py | 35 ++++++++++++++++ 8 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 users/templates/users/email_confirmation_request diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index 496f2f3f..daebf95d 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -469,7 +469,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): RadiusOption.get_attributes("non_member_attributes", attributes_kwargs), ) for user in room_user: - if user.is_ban() or user.state != User.STATE_ACTIVE: + if user.is_ban() or user.state not in [User.STATE_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: return ( sw_name, room, diff --git a/logs/views.py b/logs/views.py index 7c509134..99ecf37b 100644 --- a/logs/views.py +++ b/logs/views.py @@ -260,6 +260,16 @@ def stats_general(request): ), Club.objects.filter(state=Club.STATE_NOT_YET_ACTIVE).count(), ], + "email_not_confirmed_users": [ + _("Email not yet confirmed users"), + User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED).count(), + ( + Adherent.objects.filter( + state=Adherent.STATE_EMAIL_NOT_YET_CONFIRMED + ).count() + ), + Club.objects.filter(state=Club.STATE_EMAIL_NOT_YET_CONFIRMED).count(), + ], "adherent_users": [ _("Contributing members"), _all_adherent.count(), diff --git a/re2o/utils.py b/re2o/utils.py index f4abc57c..61c45c0c 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -116,7 +116,7 @@ def all_has_access(search_time=None, including_asso=True): if search_time is None: search_time = timezone.now() filter_user = ( - Q(state=User.STATE_ACTIVE) + (Q(state=User.STATE_ACTIVE) | Q(state=User.STATE_EMAIL_NOT_YET_CONFIRMED)) & ~Q( ban__in=Ban.objects.filter( Q(date_start__lt=search_time) & Q(date_end__gt=search_time) diff --git a/users/forms.py b/users/forms.py index 3cac1d16..bc88a1f4 100644 --- a/users/forms.py +++ b/users/forms.py @@ -117,6 +117,20 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): user.save() +class ConfirmMailForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): + """Formulaire de confirmation de l'email de l'utilisateur""" + class Meta: + model = User + fields = [] + + def save(self, commit=True): + """Confirmation de l'email""" + user = super(ConfirmMailForm, self).save(commit=False) + user.confirm_mail() + user.set_active() + user.save() + + class UserCreationForm(FormRevMixin, forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password. diff --git a/users/management/commands/archive.py b/users/management/commands/archive.py index 1e4601a0..d730cfcd 100644 --- a/users/management/commands/archive.py +++ b/users/management/commands/archive.py @@ -77,6 +77,7 @@ class Command(BaseCommand): .exclude(id__in=all_has_access(search_time=date)) .exclude(state=User.STATE_NOT_YET_ACTIVE) .exclude(state=User.STATE_FULL_ARCHIVE) + .exclude(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) ) if show: diff --git a/users/models.py b/users/models.py index 75a38aa9..f93048c2 100755 --- a/users/models.py +++ b/users/models.py @@ -176,12 +176,14 @@ class User( STATE_ARCHIVE = 2 STATE_NOT_YET_ACTIVE = 3 STATE_FULL_ARCHIVE = 4 + STATE_EMAIL_NOT_YET_CONFIRMED = 5 STATES = ( (0, _("Active")), (1, _("Disabled")), (2, _("Archived")), (3, _("Not yet active")), (4, _("Fully archived")), + (5, _("Waiting for email confirmation")), ) surname = models.CharField(max_length=255) @@ -326,6 +328,7 @@ class User( return ( self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE + or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or ( allow_archived and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE) @@ -480,7 +483,7 @@ class User( def has_access(self): """ Renvoie si un utilisateur a accès à internet """ return ( - self.state == User.STATE_ACTIVE + self.state in [User.STATE_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED] and not self.is_ban() and (self.is_connected() or self.is_whitelisted()) ) or self == AssoOption.get_cached_value("utilisateur_asso") @@ -665,6 +668,7 @@ class User( Si l'instance n'existe pas, on crée le ldapuser correspondant""" if sys.version_info[0] >= 3 and ( self.state == self.STATE_ACTIVE + or self.state == STATE_EMAIL_NOT_YET_CONFIRMED or self.state == self.STATE_ARCHIVE or self.state == self.STATE_DISABLED ): @@ -783,6 +787,34 @@ class User( ) return + def confirm_email_address_mail(self, request): + """Prend en argument un request, envoie un mail pour + confirmer l'adresse""" + req = Request() + req.type = Request.EMAIL + req.user = self + req.save() + template = loader.get_template("users/email_confirmation_request") + context = { + "name": req.user.get_full_name(), + "asso": AssoOption.get_cached_value("name"), + "asso_mail": AssoOption.get_cached_value("contact"), + "site_name": GeneralOption.get_cached_value("site_name"), + "url": request.build_absolute_uri( + reverse("users:process", kwargs={"token": req.token}) + ), + "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), + } + send_mail( + "Confirmation de l'email de %(name)s / Email confirmation for " + "%(name)s" % {"name": AssoOption.get_cached_value("name")}, + template.render(context), + GeneralOption.get_cached_value("email_from"), + [req.user.email], + fail_silently=False, + ) + return + def autoregister_machine(self, mac_address, nas_type): """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" @@ -845,6 +877,12 @@ class User( self.pwd_ntlm = hashNT(password) return + def confirm_mail(self): + """Marque l'email de l'utilisateur comme confirmé""" + # Let the "set_active" method handle + self.state = self.STATE_NOT_YET_ACTIVE + self.set_active() + @cached_property def email_address(self): if ( diff --git a/users/templates/users/email_confirmation_request b/users/templates/users/email_confirmation_request new file mode 100644 index 00000000..b3385e02 --- /dev/null +++ b/users/templates/users/email_confirmation_request @@ -0,0 +1,33 @@ +Bonjour {{ name }}, + +Vous trouverez ci-dessous une URL permettant de confirmer votre +adresse mail pour votre compte {{ site_name }}. Celui-ci vous permet de gérer l'ensemble +de vos équipements, votre compte, vos factures, et tous les services proposés sur le réseau. + + {{ url }} + +Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête. + +Ce lien expirera dans {{ expire_in }} heures. + +Respectueusement, + +L'équipe de {{ asso }} (contact : {{ asso_mail }}). + +--- + +Hello {{ name }}, + +You will find below an URL allowing you to confirm the email address of your account +on {{ site_name }}. It enables you to manage your devices, your account, your invoices, and all +the services offered on the network. + + {{ url }} + +Contact the administrators if you didn't request this. + +This link will expire in {{ expire_in }} hours. + +Regards, + +The {{ asso }} team (contact: {{ asso_mail }}). diff --git a/users/views.py b/users/views.py index 3f41a990..4f4a62d3 100644 --- a/users/views.py +++ b/users/views.py @@ -105,6 +105,7 @@ from .forms import ( ClubForm, MassArchiveForm, PassForm, + ConfirmMailForm, ResetPasswordForm, ClubAdminandMembersForm, GroupForm, @@ -126,6 +127,7 @@ def new_user(request): # Use "is False" so that if None, the email is sent if is_set_password_allowed and user.should_send_password_reset_email is False: + user.confirm_email_address_mail(request) messages.success( request, _("The user %s was created.") @@ -737,6 +739,7 @@ def mass_archive(request): .exclude(id__in=all_has_access(search_time=date)) .exclude(state=User.STATE_NOT_YET_ACTIVE) .exclude(state=User.STATE_FULL_ARCHIVE) + .exclude(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) ) if not full_archive: to_archive_list = to_archive_list.exclude(state=User.STATE_ARCHIVE) @@ -1020,6 +1023,38 @@ def process_passwd(request, req): ) +def confirm_email(request, token): + """Lien pour la confirmation de l'email""" + valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) + req = get_object_or_404(valid_reqs, token=token) + + if req.type == Request.EMAIL: + return process_email(request, req) + else: + messages.error(request, _("Error: please contact an admin.")) + redirect(reverse("index")) + + +def process_email(request, req): + """Process la confirmation de mail, renvoie le formulaire + de validation""" + user = req.user + u_form = ConfirmMailForm(request.POST or None, instance=user, user=request.user) + if u_form.is_valid(): + with transaction.atomic(), reversion.create_revision(): + u_form.save() + reversion.set_comment("Email confirmation") + req.delete() + messages.success(request, _("The email was confirmed.")) + return redirect(reverse("index")) + + return form( + {"userform": u_form, "action_name": _("Confirm the email")}, + "users/user.html", + request, + ) + + @login_required def initial_register(request): switch_ip = request.GET.get("switch_ip", None) From 4a733aaf77de74bf1fc868e25182496e89284349 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 22:22:54 +0200 Subject: [PATCH 030/490] Create disable_emailnotyetconfirmed.py --- .../commands/disable_emailnotyetconfirmed.py | 42 +++++++++++++++++++ users/models.py | 2 +- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 users/management/commands/disable_emailnotyetconfirmed.py diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py new file mode 100644 index 00000000..7c01f3fb --- /dev/null +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -0,0 +1,42 @@ +# Copyright © 2017 Gabriel Détraz +# Copyright © 2017 Lara 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 django.core.management.base import BaseCommand, CommandError + +from users.models import User +from cotisations.models import Facture +from preferences.models import OptionalUser +from datetime import timedelta + +from django.utils import timezone + + +class Command(BaseCommand): + help = "Delete non members users (not yet active)." + + def handle(self, *args, **options): + """First deleting invalid invoices, and then deleting the users""" + days = OptionalUser.get_cached_value("delete_notyetactive") + users_to_disable = ( + User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) + .filter(registered__lte=timezone.now() - timedelta(days=days)) + .distinct() + ) + print("Disabling " + str(users_to_disable.count()) + " users.") + + users_to_disable.delete() diff --git a/users/models.py b/users/models.py index f93048c2..12795a67 100755 --- a/users/models.py +++ b/users/models.py @@ -668,7 +668,7 @@ class User( Si l'instance n'existe pas, on crée le ldapuser correspondant""" if sys.version_info[0] >= 3 and ( self.state == self.STATE_ACTIVE - or self.state == STATE_EMAIL_NOT_YET_CONFIRMED + or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or self.state == self.STATE_ARCHIVE or self.state == self.STATE_DISABLED ): From b991a3f45fd2e727df1f3a551a8e25300feb8f0e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 16 Apr 2020 23:14:19 +0200 Subject: [PATCH 031/490] Add option to select number of days before disabling users --- preferences/models.py | 6 ++++++ .../templates/preferences/display_preferences.html | 10 ++++++---- .../commands/disable_emailnotyetconfirmed.py | 9 +++++---- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/preferences/models.py b/preferences/models.py index 8570e79b..e470303d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -107,6 +107,12 @@ class OptionalUser(AclMixin, PreferencesModel): "Not yet active users will be deleted after this number of days." ), ) + disable_emailnotyetconfirmed = models.IntegerField( + default=2, + help_text=_( + "Users with an email address not yet confirmed will be disabled after this number of days." + ), + ) self_adhesion = models.BooleanField( default=False, help_text=_("A new user can create their account on Re2o.") ) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 89858ddc..6d7c656a 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -125,13 +125,15 @@ with this program; if not, write to the Free Software Foundation, Inc., - - - - + + + + + +
{% include 'buttons/sort.html' with prefix='user' col="name" text=tr_name %}{% include 'buttons/sort.html' with prefix='user' col="surname" text=tr_surname %}
{% trans "All users are active by default" %} {{ useroptions.all_users_active|tick }}{% trans "Allow directly entering a password during account creation" %}{{ useroptions.allow_set_password_during_user_creation|tick }}
{% trans "Allow archived users to log in" %} {{ useroptions.allow_archived_connexion|tick }}
{% trans "All users are active by default" %} {{ useroptions.all_users_active|tick }}{% trans "Allow directly entering a password during account creation" %}{{ useroptions.allow_set_password_during_user_creation|tick }}
{% trans "Allow archived users to log in" %} {{ useroptions.allow_archived_connexion|tick }}
{% trans "Allow directly entering a password during account creation" %}{{ useroptions.allow_set_password_during_user_creation|tick }}{% trans "Disable email not yet confirmed users after" %}{% blocktrans with disable_emailnotyetconfirmed=useroptions.disable_emailnotyetconfirmed %}{{ disable_emailnotyetconfirmed }} days{% endblocktrans %}

{% trans "Users general permissions" %}

diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 7c01f3fb..404b5004 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -27,16 +27,17 @@ from django.utils import timezone class Command(BaseCommand): - help = "Delete non members users (not yet active)." + help = "Disable users who haven't confirmed their email." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("delete_notyetactive") + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_disable = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) .filter(registered__lte=timezone.now() - timedelta(days=days)) .distinct() ) print("Disabling " + str(users_to_disable.count()) + " users.") - - users_to_disable.delete() + + for user in users_to_disable: + self.state = User.STATE_DISABLED From 73d3d6b48097e5d37add9b69e498ab672e5ed643 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 00:24:35 +0200 Subject: [PATCH 032/490] Start implementing user-facing confirmation email mechanics --- users/forms.py | 22 +++++++++++++ .../commands/disable_emailnotyetconfirmed.py | 3 +- users/models.py | 6 +++- users/templates/users/profil.html | 31 ++++++++++++++++++- users/urls.py | 1 + users/views.py | 14 +++++++++ 6 files changed, 74 insertions(+), 3 deletions(-) diff --git a/users/forms.py b/users/forms.py index bc88a1f4..5669b155 100644 --- a/users/forms.py +++ b/users/forms.py @@ -299,6 +299,11 @@ class ResetPasswordForm(forms.Form): email = forms.EmailField(max_length=255) +class ResendConfirmationEmailForm(forms.Form): + """Formulaire de renvoie du mail de confirmation""" + pass + + class MassArchiveForm(forms.Form): """Formulaire d'archivage des users inactif. Prend en argument du formulaire la date de depart avant laquelle archiver les @@ -344,6 +349,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["room"].label = _("Room") self.fields["room"].empty_label = _("No room") self.fields["school"].empty_label = _("Select a school") + self.initial["email"] = kwargs["instance"].email class Meta: model = Adherent @@ -390,6 +396,22 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): remove_user_room(room) return + def save(self, commit=True): + """On met à jour l'état de l'utilisateur en fonction de son mail""" + user = super(AdherentForm, self).save(commit=False) + + if user.email != self.initial["email"]: + # Send a confirmation email + if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: + user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED + user.confirm_email_address_mail() + + # Always keep the oldest change date + if user.email_change_date is None: + user.email_change_date = timezone.now() + + return user + class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 404b5004..b2678c19 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -34,7 +34,8 @@ class Command(BaseCommand): days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_disable = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) - .filter(registered__lte=timezone.now() - timedelta(days=days)) + .exclude(email_change_date__is_null=True) + .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) print("Disabling " + str(users_to_disable.count()) + " users.") diff --git a/users/models.py b/users/models.py index 12795a67..8671fba0 100755 --- a/users/models.py +++ b/users/models.py @@ -226,6 +226,7 @@ class User( shortcuts_enabled = models.BooleanField( verbose_name=_("enable shortcuts on Re2o website"), default=True ) + email_change_date = None USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] @@ -879,7 +880,10 @@ class User( def confirm_mail(self): """Marque l'email de l'utilisateur comme confirmé""" - # Let the "set_active" method handle + # Reset the email change date + self.email_change_date = None + + # Let the "set_active" method handle the rest self.state = self.STATE_NOT_YET_ACTIVE self.set_active() diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 0bd25f75..cb8358a8 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -38,7 +38,36 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

{% endif %} +
+ {% if users.state == Users.STATE_NOT_YET_ACTIVE %} +

{% blocktrans with name=users.name surname=users.surname %}Welcome {{ name }} {{ surname }}{% endblocktrans %}

+ {% else %} +

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

+ {% endif %} +
+ + + +
+ {% if users.state == Users.STATE_NOT_YET_ACTIVE %} +
+
+ {% blocktrans %}Please confirm your email address{% endblocktrans %} +
+ + {% blocktrans %}Resend the email{% endblocktrans %} + +
+
+ {% elif users.state == Users.STATE_DISABLED %} +
+
+ {% blocktrans %}Your account has been disabled{% endblocktrans %} +
+
+ {% endif %} +
{% if users.is_ban%} @@ -181,7 +210,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Email address" %}
-
{{ users.email }}
+
{{ users.email }}{% if users.email_change_date is not None %}{% trans "Pending confirmation..." %}{% endif %}
diff --git a/users/urls.py b/users/urls.py index 1cd303e6..8ab5253d 100644 --- a/users/urls.py +++ b/users/urls.py @@ -42,6 +42,7 @@ urlpatterns = [ url(r"^state/(?P[0-9]+)$", views.state, name="state"), url(r"^groups/(?P[0-9]+)$", views.groups, name="groups"), url(r"^password/(?P[0-9]+)$", views.password, name="password"), + url(r"^confirm_email/(?P[0-9]+)$", views.resend_confirmation_email, name="resend-confirmation-email"), url( r"^del_group/(?P[0-9]+)/(?P[0-9]+)$", views.del_group, diff --git a/users/views.py b/users/views.py index 4f4a62d3..266c0717 100644 --- a/users/views.py +++ b/users/views.py @@ -107,6 +107,7 @@ from .forms import ( PassForm, ConfirmMailForm, ResetPasswordForm, + ResendConfirmationEmailForm, ClubAdminandMembersForm, GroupForm, InitialRegisterForm, @@ -1023,6 +1024,19 @@ def process_passwd(request, req): ) +def resend_confirmation_email(request): + """ Renvoie du mail de confirmation """ + userform = ResendConfirmationEmailForm(request.POST or None) + if userform.is_valid(): + request.user.confirm_email_address_mail() + messages.success(request, _("An email to confirm your address was sent.")) + return redirect(reverse("index")) + + return form( + {"userform": userform, "action_name": _("Send")}, "users/user.html", request + ) + + def confirm_email(request, token): """Lien pour la confirmation de l'email""" valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) From ac318ba20fe6b2c6f0ccc5eb718808ac4c373809 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 00:16:56 +0000 Subject: [PATCH 033/490] Fix handling of confirmation email for front facing elements --- users/forms.py | 13 ++++++---- users/models.py | 3 ++- users/templates/users/profil.html | 40 +++++++++++------------------ users/views.py | 42 ++++++++++++++++++------------- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/users/forms.py b/users/forms.py index 5669b155..c9f97508 100644 --- a/users/forms.py +++ b/users/forms.py @@ -368,6 +368,8 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): label=_("Force the move?"), initial=False, required=False ) + should_send_confirmation_email = False + def clean_email(self): if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( "email" @@ -398,18 +400,19 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): def save(self, commit=True): """On met à jour l'état de l'utilisateur en fonction de son mail""" - user = super(AdherentForm, self).save(commit=False) + user = super(AdherentForm, self).save(commit=commit) if user.email != self.initial["email"]: # Send a confirmation email if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED - user.confirm_email_address_mail() + self.should_send_confirmation_email = True - # Always keep the oldest change date - if user.email_change_date is None: - user.email_change_date = timezone.now() + # Always keep the oldest change date + if user.email_change_date is None: + user.email_change_date = timezone.now() + user.save() return user diff --git a/users/models.py b/users/models.py index 8671fba0..48c91be9 100755 --- a/users/models.py +++ b/users/models.py @@ -226,7 +226,7 @@ class User( shortcuts_enabled = models.BooleanField( verbose_name=_("enable shortcuts on Re2o website"), default=True ) - email_change_date = None + email_change_date = models.DateTimeField(default=None, null=True) USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] @@ -795,6 +795,7 @@ class User( req.type = Request.EMAIL req.user = self req.save() + template = loader.get_template("users/email_confirmation_request") context = { "name": req.user.get_full_name(), diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index cb8358a8..2cbfe9ce 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -39,35 +39,23 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %}
-
- {% if users.state == Users.STATE_NOT_YET_ACTIVE %} -

{% blocktrans with name=users.name surname=users.surname %}Welcome {{ name }} {{ surname }}{% endblocktrans %}

- {% else %} -

{% blocktrans with name=users.name surname=users.surname %}Profile of {{ name }} {{ surname }}{% endblocktrans %}

- {% endif %} + +{% if users.state == users.STATE_EMAIL_NOT_YET_CONFIRMED %} +
+ {% blocktrans %}Please confirm your email address.{% endblocktrans %} +
+ + {% blocktrans %}Didn't receive the email?{% endblocktrans %} +
- +{% elif users.state == users.STATE_DISABLED %} +
+ {% blocktrans %}Your account has been disabled{% endblocktrans %} +
+{% endif %}
- {% if users.state == Users.STATE_NOT_YET_ACTIVE %} -
-
- {% blocktrans %}Please confirm your email address{% endblocktrans %} -
- - {% blocktrans %}Resend the email{% endblocktrans %} - -
-
- {% elif users.state == Users.STATE_DISABLED %} -
-
- {% blocktrans %}Your account has been disabled{% endblocktrans %} -
-
- {% endif %} -
{% if users.is_ban%} @@ -210,7 +198,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Email address" %}
-
{{ users.email }}{% if users.email_change_date is not None %}{% trans "Pending confirmation..." %}{% endif %}
+
{{ users.email }}{% if users.email_change_date is not None %}
{% trans "Pending confirmation..." %}{% endif %}
diff --git a/users/views.py b/users/views.py index 266c0717..16acd748 100644 --- a/users/views.py +++ b/users/views.py @@ -223,8 +223,13 @@ def edit_info(request, user, userid): ) if user_form.is_valid(): if user_form.changed_data: - user_form.save() + user = user_form.save() messages.success(request, _("The user was edited.")) + + if user_form.should_send_confirmation_email: + user.confirm_email_address_mail(request) + messages.warning(request, _("Sent a new confirmation email")) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": user_form, "action_name": _("Edit")}, @@ -994,12 +999,15 @@ def reset_password(request): def process(request, token): - """Process, lien pour la reinitialisation du mot de passe""" + """Process, lien pour la reinitialisation du mot de passe + et la confirmation de l'email""" valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) req = get_object_or_404(valid_reqs, token=token) if req.type == Request.PASSWD: return process_passwd(request, req) + elif req.type == Request.EMAIL: + return process_email(request, req) else: messages.error(request, _("Error: please contact an admin.")) redirect(reverse("index")) @@ -1024,31 +1032,31 @@ def process_passwd(request, req): ) -def resend_confirmation_email(request): +def resend_confirmation_email(request, userid): """ Renvoie du mail de confirmation """ userform = ResendConfirmationEmailForm(request.POST or None) if userform.is_valid(): - request.user.confirm_email_address_mail() + try: + user = User.objects.get( + id=userid, + state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], + ) + except User.DoesNotExist: + messages.error(request, _("The user doesn't exist.")) + return form( + {"userform": userform, "action_name": _("Reset")}, + "users/user.html", + request, + ) + user.confirm_email_address_mail(request) messages.success(request, _("An email to confirm your address was sent.")) - return redirect(reverse("index")) + return redirect(reverse("users:profil", kwargs={"userid": userid})) return form( {"userform": userform, "action_name": _("Send")}, "users/user.html", request ) -def confirm_email(request, token): - """Lien pour la confirmation de l'email""" - valid_reqs = Request.objects.filter(expires_at__gt=timezone.now()) - req = get_object_or_404(valid_reqs, token=token) - - if req.type == Request.EMAIL: - return process_email(request, req) - else: - messages.error(request, _("Error: please contact an admin.")) - redirect(reverse("index")) - - def process_email(request, req): """Process la confirmation de mail, renvoie le formulaire de validation""" From 0ac4d811775a09307b8a3dfac48a4a407a66238c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 00:17:18 +0000 Subject: [PATCH 034/490] Add missing migrations --- ...tionaluser_disable_emailnotyetconfirmed.py | 21 +++++++++++++++++++ users/migrations/0085_auto_20200417_0031.py | 20 ++++++++++++++++++ .../migrations/0086_user_email_change_date.py | 20 ++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py create mode 100644 users/migrations/0085_auto_20200417_0031.py create mode 100644 users/migrations/0086_user_email_change_date.py diff --git a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py new file mode 100644 index 00000000..3cc12081 --- /dev/null +++ b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 00:46 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0068_optionaluser_allow_set_password_during_user_creation'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='disable_emailnotyetconfirmed', + field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be disabled after this number of days.') + ), + ] + diff --git a/users/migrations/0085_auto_20200417_0031.py b/users/migrations/0085_auto_20200417_0031.py new file mode 100644 index 00000000..3a2e4241 --- /dev/null +++ b/users/migrations/0085_auto_20200417_0031.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-16 22:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0084_auto_20191120_0159'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='state', + field=models.IntegerField(choices=[(0, 'Active'), (1, 'Disabled'), (2, 'Archived'), (3, 'Not yet active'), (4, 'Fully archived'), (5, 'Waiting for email confirmation')], default=3), + ), + ] diff --git a/users/migrations/0086_user_email_change_date.py b/users/migrations/0086_user_email_change_date.py new file mode 100644 index 00000000..b6f7075c --- /dev/null +++ b/users/migrations/0086_user_email_change_date.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 00:00 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0085_auto_20200417_0031'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='email_change_date', + field=models.DateTimeField(default=None, null=True), + ), + ] From b190549618b89d26535961549f167b6a9a3c063d Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:22:29 +0200 Subject: [PATCH 035/490] Improve template for resending a confirmation email --- users/forms.py | 5 --- users/models.py | 2 +- .../users/resend_confirmation_email.html | 44 +++++++++++++++++++ users/views.py | 29 ++++++------ 4 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 users/templates/users/resend_confirmation_email.html diff --git a/users/forms.py b/users/forms.py index c9f97508..79302182 100644 --- a/users/forms.py +++ b/users/forms.py @@ -299,11 +299,6 @@ class ResetPasswordForm(forms.Form): email = forms.EmailField(max_length=255) -class ResendConfirmationEmailForm(forms.Form): - """Formulaire de renvoie du mail de confirmation""" - pass - - class MassArchiveForm(forms.Form): """Formulaire d'archivage des users inactif. Prend en argument du formulaire la date de depart avant laquelle archiver les diff --git a/users/models.py b/users/models.py index 48c91be9..0ca94a37 100755 --- a/users/models.py +++ b/users/models.py @@ -339,7 +339,7 @@ class User( def set_active(self): """Enable this user if he subscribed successfully one time before Reenable it if it was archived - Do nothing if disabed""" + Do nothing if disabled or waiting for email confirmation""" if self.state == self.STATE_NOT_YET_ACTIVE: if self.facture_set.filter(valid=True).filter( Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion") diff --git a/users/templates/users/resend_confirmation_email.html b/users/templates/users/resend_confirmation_email.html new file mode 100644 index 00000000..2c086ccd --- /dev/null +++ b/users/templates/users/resend_confirmation_email.html @@ -0,0 +1,44 @@ +{% extends 'users/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 Lara 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 "Confirmation email" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Re-send confirmation email{% endblocktrans %}

+

{% blocktrans %}The confirmation email will be sent to {{ email }}.{% endblocktrans %}

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %} +
+
+
+
+{% endblock %} + diff --git a/users/views.py b/users/views.py index 16acd748..5c23932e 100644 --- a/users/views.py +++ b/users/views.py @@ -107,7 +107,6 @@ from .forms import ( PassForm, ConfirmMailForm, ResetPasswordForm, - ResendConfirmationEmailForm, ClubAdminandMembersForm, GroupForm, InitialRegisterForm, @@ -228,7 +227,7 @@ def edit_info(request, user, userid): if user_form.should_send_confirmation_email: user.confirm_email_address_mail(request) - messages.warning(request, _("Sent a new confirmation email")) + messages.success(request, _("Sent a new confirmation email")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( @@ -1034,26 +1033,24 @@ def process_passwd(request, req): def resend_confirmation_email(request, userid): """ Renvoie du mail de confirmation """ - userform = ResendConfirmationEmailForm(request.POST or None) + try: + user = User.objects.get( + id=userid, + state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], + ) + except User.DoesNotExist: + messages.error(request, _("The user doesn't exist.")) + return redirect(reverse("users:profil", kwargs={"userid": userid})) + if userform.is_valid(): - try: - user = User.objects.get( - id=userid, - state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], - ) - except User.DoesNotExist: - messages.error(request, _("The user doesn't exist.")) - return form( - {"userform": userform, "action_name": _("Reset")}, - "users/user.html", - request, - ) user.confirm_email_address_mail(request) messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) return form( - {"userform": userform, "action_name": _("Send")}, "users/user.html", request + {"email": user.email}, + "users/resend_confirmation_email.html", + request, ) From c4acc0f6f2ab0a65cf7c3ebc345cbf183152c2f9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:30:17 +0200 Subject: [PATCH 036/490] Fix template shown when confirming send an email --- users/views.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/users/views.py b/users/views.py index 5c23932e..29bdb500 100644 --- a/users/views.py +++ b/users/views.py @@ -1032,25 +1032,22 @@ def process_passwd(request, req): def resend_confirmation_email(request, userid): - """ Renvoie du mail de confirmation """ - try: - user = User.objects.get( - id=userid, - state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], - ) - except User.DoesNotExist: - messages.error(request, _("The user doesn't exist.")) - return redirect(reverse("users:profil", kwargs={"userid": userid})) + """ Renvoi du mail de confirmation """ + if request.method == "POST": + try: + user = User.objects.get( + id=userid, + state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], + ) + user.confirm_email_address_mail(request) + messages.success(request, _("An email to confirm your address was sent.")) + except User.DoesNotExist: + messages.error(request, _("The user doesn't exist.")) - if userform.is_valid(): - user.confirm_email_address_mail(request) - messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) return form( - {"email": user.email}, - "users/resend_confirmation_email.html", - request, + {"email": user.email}, "users/resend_confirmation_email.html", request ) From e5b8cc48fac859f9327dff7a51e8a03232998d82 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:34:54 +0200 Subject: [PATCH 037/490] Fix user referenced before assignment --- users/views.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/users/views.py b/users/views.py index 29bdb500..4ed046f3 100644 --- a/users/views.py +++ b/users/views.py @@ -1033,17 +1033,17 @@ def process_passwd(request, req): def resend_confirmation_email(request, userid): """ Renvoi du mail de confirmation """ - if request.method == "POST": - try: - user = User.objects.get( - id=userid, - state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], - ) - user.confirm_email_address_mail(request) - messages.success(request, _("An email to confirm your address was sent.")) - except User.DoesNotExist: - messages.error(request, _("The user doesn't exist.")) + try: + user = User.objects.get( + id=userid, + state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], + ) + except User.DoesNotExist: + messages.error(request, _("The user doesn't exist.")) + if request.method == "POST": + user.confirm_email_address_mail(request) + messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) return form( From 3bee3340627fc4e8851fc77454f19216ed1be56c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:38:05 +0200 Subject: [PATCH 038/490] Improve template of email confirmation view --- users/templates/users/resend_confirmation_email.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users/templates/users/resend_confirmation_email.html b/users/templates/users/resend_confirmation_email.html index 2c086ccd..e0f782e0 100644 --- a/users/templates/users/resend_confirmation_email.html +++ b/users/templates/users/resend_confirmation_email.html @@ -32,8 +32,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %} -

{% blocktrans %}Re-send confirmation email{% endblocktrans %}

-

{% blocktrans %}The confirmation email will be sent to {{ email }}.{% endblocktrans %}

+

{% blocktrans %}Re-send confirmation email?{% endblocktrans %}

+

{% blocktrans %}The confirmation email will be sent to{% endblocktrans %} {{ email }}.

{% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %}
From 7b4ec26d94ec5d7b92e107ffd8002036513fa66a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:25:42 +0200 Subject: [PATCH 039/490] Replace ConfirmMailForm with an html template --- users/forms.py | 14 -------- users/templates/users/confirm_email.html | 44 ++++++++++++++++++++++++ users/views.py | 20 ++++++----- 3 files changed, 55 insertions(+), 23 deletions(-) create mode 100644 users/templates/users/confirm_email.html diff --git a/users/forms.py b/users/forms.py index 79302182..f3ea52de 100644 --- a/users/forms.py +++ b/users/forms.py @@ -117,20 +117,6 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): user.save() -class ConfirmMailForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): - """Formulaire de confirmation de l'email de l'utilisateur""" - class Meta: - model = User - fields = [] - - def save(self, commit=True): - """Confirmation de l'email""" - user = super(ConfirmMailForm, self).save(commit=False) - user.confirm_mail() - user.set_active() - user.save() - - class UserCreationForm(FormRevMixin, forms.ModelForm): """A form for creating new users. Includes all the required fields, plus a repeated password. diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html new file mode 100644 index 00000000..61ae250e --- /dev/null +++ b/users/templates/users/confirm_email.html @@ -0,0 +1,44 @@ +{% extends 'users/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 Lara 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 "Confirmation email" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Confirmation email{% endblocktrans %}

+

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for user {{ firstname }} {{ lastname }}.{% endblocktrans %}.

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %} +
+
+
+
+{% endblock %} + diff --git a/users/views.py b/users/views.py index 4ed046f3..5b8b8e47 100644 --- a/users/views.py +++ b/users/views.py @@ -105,7 +105,6 @@ from .forms import ( ClubForm, MassArchiveForm, PassForm, - ConfirmMailForm, ResetPasswordForm, ClubAdminandMembersForm, GroupForm, @@ -1047,7 +1046,9 @@ def resend_confirmation_email(request, userid): return redirect(reverse("users:profil", kwargs={"userid": userid})) return form( - {"email": user.email}, "users/resend_confirmation_email.html", request + {"email": user.email}, + "users/resend_confirmation_email.html", + request ) @@ -1055,19 +1056,20 @@ def process_email(request, req): """Process la confirmation de mail, renvoie le formulaire de validation""" user = req.user - u_form = ConfirmMailForm(request.POST or None, instance=user, user=request.user) - if u_form.is_valid(): + if request.method == "POST": with transaction.atomic(), reversion.create_revision(): - u_form.save() + user.confirm_mail() + user.save() reversion.set_comment("Email confirmation") + req.delete() - messages.success(request, _("The email was confirmed.")) + messages.success(request, _("The %(email)s address was confirmed." % user.email)) return redirect(reverse("index")) return form( - {"userform": u_form, "action_name": _("Confirm the email")}, - "users/user.html", - request, + {"email": user.email, "firstname": user.firstname, "lastname": user.surname}, + "users/confirm_email.html", + request ) From 1124801b7da37f82d945b1d1902b94f4d60479bb Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 10:28:04 +0000 Subject: [PATCH 040/490] Fix confirm email template --- users/templates/users/confirm_email.html | 2 +- users/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html index 61ae250e..c90af524 100644 --- a/users/templates/users/confirm_email.html +++ b/users/templates/users/confirm_email.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %}

{% blocktrans %}Confirmation email{% endblocktrans %}

-

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for user {{ firstname }} {{ lastname }}.{% endblocktrans %}.

+

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for user {{ firstname }} {{ lastname }}.{% endblocktrans %}

{% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %}
diff --git a/users/views.py b/users/views.py index 5b8b8e47..42dc48e2 100644 --- a/users/views.py +++ b/users/views.py @@ -1067,7 +1067,7 @@ def process_email(request, req): return redirect(reverse("index")) return form( - {"email": user.email, "firstname": user.firstname, "lastname": user.surname}, + {"email": user.email, "firstname": user.name, "lastname": user.surname}, "users/confirm_email.html", request ) From bf69f71b9e7aac128b9c4d0021550b1972a28291 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 10:30:44 +0000 Subject: [PATCH 041/490] Fix string formatting error during email confirmation --- users/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/views.py b/users/views.py index 42dc48e2..233ddaee 100644 --- a/users/views.py +++ b/users/views.py @@ -1063,7 +1063,7 @@ def process_email(request, req): reversion.set_comment("Email confirmation") req.delete() - messages.success(request, _("The %(email)s address was confirmed." % user.email)) + messages.success(request, _("The %s address was confirmed." % user.email)) return redirect(reverse("index")) return form( From d4f13f88325d9bb5b247ac3e6afd8a6e0f0864dc Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:41:33 +0200 Subject: [PATCH 042/490] Handle manually switching user state to/from STATE_EMAIL_NOT_YET_CONFIRMED --- users/models.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/users/models.py b/users/models.py index 0ca94a37..467056e7 100755 --- a/users/models.py +++ b/users/models.py @@ -638,7 +638,7 @@ class User( self.ldap_sync() def state_sync(self): - """Archive, or unarchive, if the user was not active/or archived before""" + """Handle archiving/unarchiving, and manually confirming a user's email address""" if ( self.__original_state != self.STATE_ACTIVE and self.state == self.STATE_ACTIVE @@ -654,6 +654,16 @@ class User( and self.state == self.STATE_FULL_ARCHIVE ): self.full_archive() + elif ( + self.__original_state == self.STATE_EMAIL_NOT_YET_CONFIRMED + and self.state not in [self.STATE_EMAIL_NOT_YET_CONFIRMED, self.STATE_DISABLED] + ): + self.email_change_date = None + elif ( + self.__original_state != self.STATE_EMAIL_NOT_YET_CONFIRMED + and self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED + ): + self.email_change_date = timezone.now() def ldap_sync( self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False From b7021f32cb71016df2a43651589fd53eeff36b79 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:50:22 +0200 Subject: [PATCH 043/490] Automatically consider email valid when user is set to STATE_ACTIVE --- users/models.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/users/models.py b/users/models.py index 467056e7..7f2481cb 100755 --- a/users/models.py +++ b/users/models.py @@ -654,16 +654,15 @@ class User( and self.state == self.STATE_FULL_ARCHIVE ): self.full_archive() - elif ( - self.__original_state == self.STATE_EMAIL_NOT_YET_CONFIRMED - and self.state not in [self.STATE_EMAIL_NOT_YET_CONFIRMED, self.STATE_DISABLED] - ): - self.email_change_date = None elif ( self.__original_state != self.STATE_EMAIL_NOT_YET_CONFIRMED and self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED ): self.email_change_date = timezone.now() + elif ( + self.state == self.STATE_ACTIVE + ): + self.email_change_date = None def ldap_sync( self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False From e0de60622128cd285cd2ae4738cec2a43f6a6552 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:53:03 +0200 Subject: [PATCH 044/490] Fix overlapping conditions in User.state_sync --- users/models.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/users/models.py b/users/models.py index 7f2481cb..0f04aeaf 100755 --- a/users/models.py +++ b/users/models.py @@ -643,6 +643,7 @@ class User( self.__original_state != self.STATE_ACTIVE and self.state == self.STATE_ACTIVE ): + self.email_change_date = None self.unarchive() elif ( self.__original_state != self.STATE_ARCHIVE @@ -659,10 +660,6 @@ class User( and self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED ): self.email_change_date = timezone.now() - elif ( - self.state == self.STATE_ACTIVE - ): - self.email_change_date = None def ldap_sync( self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False From 80124695ad967021abbddc1f05846a5428f116d4 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 13:01:10 +0200 Subject: [PATCH 045/490] Move user email_change_date update on manual state change to seperate method --- users/forms.py | 1 + users/models.py | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/users/forms.py b/users/forms.py index f3ea52de..be248214 100644 --- a/users/forms.py +++ b/users/forms.py @@ -675,6 +675,7 @@ class StateForm(FormRevMixin, ModelForm): if self.cleaned_data["state"]: user.state = self.cleaned_data.get("state") user.state_sync() + user.email_change_date_sync() user.save() diff --git a/users/models.py b/users/models.py index 0f04aeaf..12f88c56 100755 --- a/users/models.py +++ b/users/models.py @@ -638,12 +638,11 @@ class User( self.ldap_sync() def state_sync(self): - """Handle archiving/unarchiving, and manually confirming a user's email address""" + """Archive, or unarchive, if the user was not active/or archived before""" if ( self.__original_state != self.STATE_ACTIVE and self.state == self.STATE_ACTIVE ): - self.email_change_date = None self.unarchive() elif ( self.__original_state != self.STATE_ARCHIVE @@ -655,11 +654,21 @@ class User( and self.state == self.STATE_FULL_ARCHIVE ): self.full_archive() + + def email_change_date_sync(self): + """Update user's email_change_date based on state update""" + if ( + self.__original_state != self.STATE_ACTIVE + and self.state == self.STATE_ACTIVE + ): + self.email_change_date = None + self.save() elif ( self.__original_state != self.STATE_EMAIL_NOT_YET_CONFIRMED and self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED ): self.email_change_date = timezone.now() + self.save() def ldap_sync( self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False From 2793e207c03fe0e3b0264d5987117e37a955df62 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:13:56 +0000 Subject: [PATCH 046/490] Always sync email_change_date on manual state change --- users/forms.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/users/forms.py b/users/forms.py index be248214..de229fcd 100644 --- a/users/forms.py +++ b/users/forms.py @@ -675,7 +675,8 @@ class StateForm(FormRevMixin, ModelForm): if self.cleaned_data["state"]: user.state = self.cleaned_data.get("state") user.state_sync() - user.email_change_date_sync() + + user.email_change_date_sync() user.save() From 32c1f8d50a065527fcb7f79a1a81924b156ec628 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 13:17:18 +0200 Subject: [PATCH 047/490] Require user_edit permission to resend confirmation email --- users/views.py | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/users/views.py b/users/views.py index 233ddaee..b93bf7f3 100644 --- a/users/views.py +++ b/users/views.py @@ -1030,6 +1030,28 @@ def process_passwd(request, req): ) +def process_email(request, req): + """Process la confirmation de mail, renvoie le formulaire + de validation""" + user = req.user + if request.method == "POST": + with transaction.atomic(), reversion.create_revision(): + user.confirm_mail() + user.save() + reversion.set_comment("Email confirmation") + + req.delete() + messages.success(request, _("The %s address was confirmed." % user.email)) + return redirect(reverse("index")) + + return form( + {"email": user.email, "firstname": user.name, "lastname": user.surname}, + "users/confirm_email.html", + request + ) + + +@can_edit(User) def resend_confirmation_email(request, userid): """ Renvoi du mail de confirmation """ try: @@ -1052,27 +1074,6 @@ def resend_confirmation_email(request, userid): ) -def process_email(request, req): - """Process la confirmation de mail, renvoie le formulaire - de validation""" - user = req.user - if request.method == "POST": - with transaction.atomic(), reversion.create_revision(): - user.confirm_mail() - user.save() - reversion.set_comment("Email confirmation") - - req.delete() - messages.success(request, _("The %s address was confirmed." % user.email)) - return redirect(reverse("index")) - - return form( - {"email": user.email, "firstname": user.name, "lastname": user.surname}, - "users/confirm_email.html", - request - ) - - @login_required def initial_register(request): switch_ip = request.GET.get("switch_ip", None) From e574284936d88d5298e6bc4acdc2908cf2d02870 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 11:39:11 +0000 Subject: [PATCH 048/490] Require login on confirmation email resend --- users/forms.py | 8 ++++++-- users/views.py | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/users/forms.py b/users/forms.py index de229fcd..dd9ddaa7 100644 --- a/users/forms.py +++ b/users/forms.py @@ -330,7 +330,11 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["room"].label = _("Room") self.fields["room"].empty_label = _("No room") self.fields["school"].empty_label = _("Select a school") - self.initial["email"] = kwargs["instance"].email + + if not kwargs["user"].is_anonymous(): + self.initial["email"] = kwargs["user"].email + else: + self.initial["email"] = None class Meta: model = Adherent @@ -383,7 +387,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """On met à jour l'état de l'utilisateur en fonction de son mail""" user = super(AdherentForm, self).save(commit=commit) - if user.email != self.initial["email"]: + if self.initial["email"] is not None and user.email != self.initial["email"]: # Send a confirmation email if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED diff --git a/users/views.py b/users/views.py index b93bf7f3..cd135546 100644 --- a/users/views.py +++ b/users/views.py @@ -1051,8 +1051,9 @@ def process_email(request, req): ) +@login_required @can_edit(User) -def resend_confirmation_email(request, userid): +def resend_confirmation_email(request, logged_user, userid): """ Renvoi du mail de confirmation """ try: user = User.objects.get( From 02040bfae925d81ab21016be08167ae54ed4c2e0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 13:43:04 +0200 Subject: [PATCH 049/490] Delete disabled users who never created an invoice --- users/management/commands/clean_notyetactive.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index 994abfc2..d6c9a701 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -17,6 +17,7 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # from django.core.management.base import BaseCommand, CommandError +from django.db.models import Q from users.models import User from cotisations.models import Facture @@ -27,13 +28,13 @@ from django.utils import timezone class Command(BaseCommand): - help = "Delete non members users (not yet active)." + help = "Delete non members users (not yet active or disabled too long ago without an invoice)." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" days = OptionalUser.get_cached_value("delete_notyetactive") users_to_delete = ( - User.objects.filter(state=User.STATE_NOT_YET_ACTIVE) + User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_DISABLED)) .filter(registered__lte=timezone.now() - timedelta(days=days)) .exclude(facture__valid=True) .distinct() From 65a91c11100d68f61a7ec69e7bda4fbcd10a1005 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 14:08:54 +0200 Subject: [PATCH 050/490] Show warning with delay before account suspension for users --- users/management/commands/clean_notyetactive.py | 2 +- users/models.py | 9 +++++++++ users/templates/users/email_confirmation_request | 6 ++++++ users/templates/users/profil.html | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index d6c9a701..d1857096 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -32,7 +32,7 @@ class Command(BaseCommand): def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("delete_notyetactive") + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_delete = ( User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_DISABLED)) .filter(registered__lte=timezone.now() - timedelta(days=days)) diff --git a/users/models.py b/users/models.py index 12f88c56..1e0db421 100755 --- a/users/models.py +++ b/users/models.py @@ -62,6 +62,7 @@ from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db import transaction from django.utils import timezone +from datetime import timedelta from django.contrib.auth.models import ( AbstractBaseUser, BaseUserManager, @@ -803,6 +804,13 @@ class User( ) return + def confirm_email_before_date(self): + if self.email_change_date is None or self.state != self.STATE_EMAIL_NOT_YET_CONFIRMED: + return None + + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + return str(self.email_change_date + timedelta(days=days)) + def confirm_email_address_mail(self, request): """Prend en argument un request, envoie un mail pour confirmer l'adresse""" @@ -821,6 +829,7 @@ class User( reverse("users:process", kwargs={"token": req.token}) ), "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), + "confirm_before": self.confirm_email_before_date(), } send_mail( "Confirmation de l'email de %(name)s / Email confirmation for " diff --git a/users/templates/users/email_confirmation_request b/users/templates/users/email_confirmation_request index b3385e02..65e20c13 100644 --- a/users/templates/users/email_confirmation_request +++ b/users/templates/users/email_confirmation_request @@ -9,6 +9,9 @@ de vos équipements, votre compte, vos factures, et tous les services proposés Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête. Ce lien expirera dans {{ expire_in }} heures. +S'il a expiré, vous pouvez renvoyer un mail de confirmation depuis votre compte {{ site_name }}. + +Attention : Si vous ne confirmez pas votre email avant le {{ confirm_before }}, votre compte sera suspendu. Respectueusement, @@ -27,6 +30,9 @@ the services offered on the network. Contact the administrators if you didn't request this. This link will expire in {{ expire_in }} hours. +If it has expired, you can send a new confirmation email from your account on {{ site_name }}. + +Warning: If you do not confirm your email before {{ confirm_before }}, your account will be suspended. Regards, diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 2cbfe9ce..84871045 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if users.state == users.STATE_EMAIL_NOT_YET_CONFIRMED %}
- {% blocktrans %}Please confirm your email address.{% endblocktrans %} + {% blocktrans %}Please confirm your email address before {{ users.confirm_email_before_date }}, or your account will be suspended.{% endblocktrans %}
{% blocktrans %}Didn't receive the email?{% endblocktrans %} From 9dad76818625537dd4eb4889cb4cba3914396aff Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 14:37:52 +0200 Subject: [PATCH 051/490] Show users in state STATE_EMAIL_NOT_YET_CONFIRMED in search --- search/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/search/forms.py b/search/forms.py index 9f2ff82a..741a15e8 100644 --- a/search/forms.py +++ b/search/forms.py @@ -35,6 +35,7 @@ CHOICES_USER = ( ("2", _("Archived")), ("3", _("Not yet active")), ("4", _("Fully archived")), + ("5", _("Waiting for email confirmation")), ) CHOICES_AFF = ( From e0609d27dd1a0a5f846d65b2bf9ce50b9993c056 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:53:51 +0000 Subject: [PATCH 052/490] Fix wrong email showing up when editing user --- users/forms.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/users/forms.py b/users/forms.py index dd9ddaa7..24657fc2 100644 --- a/users/forms.py +++ b/users/forms.py @@ -330,11 +330,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["room"].label = _("Room") self.fields["room"].empty_label = _("No room") self.fields["school"].empty_label = _("Select a school") - - if not kwargs["user"].is_anonymous(): - self.initial["email"] = kwargs["user"].email - else: - self.initial["email"] = None + self.is_anon = kwargs["user"].is_anonymous() class Meta: model = Adherent @@ -387,7 +383,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """On met à jour l'état de l'utilisateur en fonction de son mail""" user = super(AdherentForm, self).save(commit=commit) - if self.initial["email"] is not None and user.email != self.initial["email"]: + if not self.is_anon and self.initial["email"] and user.email != self.initial["email"]: # Send a confirmation email if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED From 0c1cf9ac946b3abdfa292b9b228ae06696cb2ec1 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 12:54:28 +0000 Subject: [PATCH 053/490] Improve various templates related to email confirmation --- users/models.py | 5 +++-- users/templates/users/confirm_email.html | 2 +- users/templates/users/email_confirmation_request | 4 ++-- users/templates/users/profil.html | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/users/models.py b/users/models.py index 1e0db421..6b570337 100755 --- a/users/models.py +++ b/users/models.py @@ -809,7 +809,7 @@ class User( return None days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") - return str(self.email_change_date + timedelta(days=days)) + return self.email_change_date + timedelta(days=days) def confirm_email_address_mail(self, request): """Prend en argument un request, envoie un mail pour @@ -829,7 +829,8 @@ class User( reverse("users:process", kwargs={"token": req.token}) ), "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), - "confirm_before": self.confirm_email_before_date(), + "confirm_before_fr": self.confirm_email_before_date().strftime("%d/%m/%Y"), + "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), } send_mail( "Confirmation de l'email de %(name)s / Email confirmation for " diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html index c90af524..6d562377 100644 --- a/users/templates/users/confirm_email.html +++ b/users/templates/users/confirm_email.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %}

{% blocktrans %}Confirmation email{% endblocktrans %}

-

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for user {{ firstname }} {{ lastname }}.{% endblocktrans %}

+

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for {{ firstname }} {{ lastname }}.{% endblocktrans %}

{% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %}
diff --git a/users/templates/users/email_confirmation_request b/users/templates/users/email_confirmation_request index 65e20c13..8c5fe03d 100644 --- a/users/templates/users/email_confirmation_request +++ b/users/templates/users/email_confirmation_request @@ -11,7 +11,7 @@ Contactez les administrateurs si vous n'êtes pas à l'origine de cette requête Ce lien expirera dans {{ expire_in }} heures. S'il a expiré, vous pouvez renvoyer un mail de confirmation depuis votre compte {{ site_name }}. -Attention : Si vous ne confirmez pas votre email avant le {{ confirm_before }}, votre compte sera suspendu. +/!\ Attention : Si vous ne confirmez pas votre email avant le {{ confirm_before_fr }}, votre compte sera suspendu. Respectueusement, @@ -32,7 +32,7 @@ Contact the administrators if you didn't request this. This link will expire in {{ expire_in }} hours. If it has expired, you can send a new confirmation email from your account on {{ site_name }}. -Warning: If you do not confirm your email before {{ confirm_before }}, your account will be suspended. +/!\ Warning: If you do not confirm your email before {{ confirm_before_en }}, your account will be suspended. Regards, diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 84871045..1e52d61a 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if users.state == users.STATE_EMAIL_NOT_YET_CONFIRMED %}
- {% blocktrans %}Please confirm your email address before {{ users.confirm_email_before_date }}, or your account will be suspended.{% endblocktrans %} + {% blocktrans with confirm_before_date=users.confirm_email_before_date|date:"DATE_FORMAT" %}Please confirm your email address before {{ confirm_before_date }}, or your account will be suspended.{% endblocktrans %}
{% blocktrans %}Didn't receive the email?{% endblocktrans %} From 0a8ed0bb40c061cd5be53199bc54abdfa4b23027 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 13:14:32 +0000 Subject: [PATCH 054/490] Fix disable_emailnotyetconfirmed task --- users/management/commands/disable_emailnotyetconfirmed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index b2678c19..e6de32f3 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -34,11 +34,11 @@ class Command(BaseCommand): days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_disable = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) - .exclude(email_change_date__is_null=True) .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) print("Disabling " + str(users_to_disable.count()) + " users.") for user in users_to_disable: - self.state = User.STATE_DISABLED + user.state = User.STATE_DISABLED + user.save() From c71c41fea8149f26b037ab484d7e43454739f034 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 15:16:26 +0200 Subject: [PATCH 055/490] Notify users of suspension when they failed to confirm their email --- .../commands/disable_emailnotyetconfirmed.py | 1 + users/models.py | 18 +++++++++++++++++ users/templates/users/email_disable_notif | 20 +++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 users/templates/users/email_disable_notif diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index e6de32f3..89dd3e86 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -41,4 +41,5 @@ class Command(BaseCommand): for user in users_to_disable: user.state = User.STATE_DISABLED + user.notif_disable() user.save() diff --git a/users/models.py b/users/models.py index 6b570337..e64db5d1 100755 --- a/users/models.py +++ b/users/models.py @@ -895,6 +895,24 @@ class User( ) return + def notif_disable(self): + """Envoi un mail de notification informant que l'adresse mail n'a pas été confirmée""" + template = loader.get_template("users/email_disable_notif") + context = { + "name": self.get_full_name(), + "asso_name": AssoOption.get_cached_value("name"), + "asso_email": AssoOption.get_cached_value("contact"), + "site_name": GeneralOption.get_cached_value("site_name"), + } + send_mail( + "Suspension automatique / Automatic suspension", + template.render(context), + GeneralOption.get_cached_value("email_from"), + [self.email], + fail_silently=False, + ) + return + def set_password(self, password): """ A utiliser de préférence, set le password en hash courrant et dans la version ntlm""" diff --git a/users/templates/users/email_disable_notif b/users/templates/users/email_disable_notif new file mode 100644 index 00000000..5b2a90e0 --- /dev/null +++ b/users/templates/users/email_disable_notif @@ -0,0 +1,20 @@ +Bonjour {{ name }}, + +Votre connexion a été automatiquement suspendue car votre adresse mail n'a pas été confirmée. Vous pouvez renvoyer un mail de confirmation sur votre compte {{ site_name }} pour réactiver votre connexion. + +Pour de plus amples renseignements, contactez {{ asso_name }} à l'adresse {{ asso_mail }}. + +Respectueusement, +L'équipe de {{ asso_name }}. + +--- + +Hello {{ name }}, + +Your connection has automatically been suspended because you have not confirmed your email address. You can ask for a new confirmation email to be sent on your profil at {{ site_name }} to enable your connection. + + +For more information, contactez {{ asso_name }} at {{ asso_mail }}. + +Regards, +The {{ asso_name }} team. From 263d5cfb4c61e3fe90944aa795d209d1670e185e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 15:22:53 +0200 Subject: [PATCH 056/490] Use get_full_name to generate confirm_email.html --- users/templates/users/confirm_email.html | 2 +- users/views.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html index 6d562377..60bcf11d 100644 --- a/users/templates/users/confirm_email.html +++ b/users/templates/users/confirm_email.html @@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% csrf_token %}

{% blocktrans %}Confirmation email{% endblocktrans %}

-

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for {{ firstname }} {{ lastname }}.{% endblocktrans %}

+

{% blocktrans %}Confirm the email{% endblocktrans %} {{ email }} {% blocktrans %}for {{ name }}.{% endblocktrans %}

{% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type="submit" icon="ok" button_class="btn-success" %}
diff --git a/users/views.py b/users/views.py index cd135546..19d7d97f 100644 --- a/users/views.py +++ b/users/views.py @@ -1045,7 +1045,7 @@ def process_email(request, req): return redirect(reverse("index")) return form( - {"email": user.email, "firstname": user.name, "lastname": user.surname}, + {"email": user.email, "name": user.get_full_name()}, "users/confirm_email.html", request ) From 14fe70c7621fb7b38600acaec4cbf10522880f12 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 16:03:37 +0200 Subject: [PATCH 057/490] Add missing translations --- logs/locale/fr/LC_MESSAGES/django.po | 4 + logs/views.py | 2 +- preferences/forms.py | 1 + preferences/locale/fr/LC_MESSAGES/django.po | 19 ++++- .../preferences/display_preferences.html | 2 +- search/locale/fr/LC_MESSAGES/django.po | 4 + users/forms.py | 3 + users/locale/fr/LC_MESSAGES/django.po | 73 ++++++++++++++++++- users/templates/users/profil.html | 2 +- users/views.py | 4 +- 10 files changed, 103 insertions(+), 11 deletions(-) diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index f04f6bef..92dbb89b 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -244,6 +244,10 @@ msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" #: logs/views.py:264 +msgid "Waiting for email confirmation users" +msgstr "Utilisateurs en attente de confirmation d'email" + +#: logs/views.py:273 msgid "Contributing members" msgstr "Adhérents cotisants" diff --git a/logs/views.py b/logs/views.py index 99ecf37b..615fb69b 100644 --- a/logs/views.py +++ b/logs/views.py @@ -261,7 +261,7 @@ def stats_general(request): Club.objects.filter(state=Club.STATE_NOT_YET_ACTIVE).count(), ], "email_not_confirmed_users": [ - _("Email not yet confirmed users"), + _("Waiting for email confirmation users"), User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED).count(), ( Adherent.objects.filter( diff --git a/preferences/forms.py b/preferences/forms.py index 2591b73b..b296c0d4 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -66,6 +66,7 @@ class EditOptionalUserForm(ModelForm): self.fields["gpg_fingerprint"].label = _("GPG fingerprint") self.fields["all_can_create_club"].label = _("All can create a club") self.fields["all_can_create_adherent"].label = _("All can create a member") + self.fields["disable_emailnotyetconfirmed"].label = _("Delay before disabling accounts without a verified email") self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index deb77b22..e7ac2e66 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -286,6 +286,14 @@ msgstr "" "jours." #: preferences/models.py:111 +msgid "" +"Users with an email address not yet confirmed will be disabled after" +" this number of days." +msgstr "" +"Les utilisateurs n'ayant pas confirmé leur addresse mail seront" +" désactivés après ce nombre de jours" + +#: preferences/models.py:114 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." @@ -1066,14 +1074,19 @@ msgstr "%(delete_notyetactive)s jours" msgid "All users are active by default" msgstr "Tous les utilisateurs sont actifs par défault" -msgid "Allow directly entering a password during account creation" -msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" - #: preferences/templates/preferences/display_preferences.html:130 msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" #: preferences/templates/preferences/display_preferences.html:133 +msgid "Allow directly entering a password during account creation" +msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" + +#: preferences/templates/preferences/display_preferences.html:136 +msgid "Delay before disabling accounts without a verified email" +msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" + +#: preferences/templates/preferences/display_preferences.html:136 msgid "Users general permissions" msgstr "Permissions générales des utilisateurs" diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 6d7c656a..8eb75918 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -131,7 +131,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Allow directly entering a password during account creation" %} {{ useroptions.allow_set_password_during_user_creation|tick }} - {% trans "Disable email not yet confirmed users after" %} + {% trans "Delay before disabling accounts without a verified email" %} {% blocktrans with disable_emailnotyetconfirmed=useroptions.disable_emailnotyetconfirmed %}{{ disable_emailnotyetconfirmed }} days{% endblocktrans %} diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index ca0507ae..6da1a477 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -50,6 +50,10 @@ msgstr "Pas encore adhéré" msgid "Fully archived" msgstr "Complètement archivés" +#: search/forms.py:38 +msgid "Waiting for email confirmation" +msgstr "En attente de confirmation d'email" + #: search/forms.py:41 msgid "Users" msgstr "Utilisateurs" diff --git a/users/forms.py b/users/forms.py index 24657fc2..da833203 100644 --- a/users/forms.py +++ b/users/forms.py @@ -409,6 +409,9 @@ class AdherentCreationForm(AdherentForm): " your initial password by email. If you do not have" " any means of accessing your emails, you can disable" " this option to set your password immediatly." + " You will still receive an email to confirm your address." + " Failure to confirm your address will result in an" + " automatic suspension of your account until you do." ) init_password_by_mail = forms.BooleanField( diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 4d8794b6..1d6ec63b 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -163,11 +163,17 @@ msgid "" " your initial password by email. If you do not have" " any means of accessing your emails, you can disable" " this option to set your password immediatly." +" You will still receive an email to confirm your address." +" Failure to confirm your address will result in an" +" automatic suspension of your account until you do." msgstr "" "Si cette option est activée, vous recevrez un lien pour choisir" " votre mot de passe par email. Si vous n'avez pas accès" " à vos mails, vous pouvez désactiver cette option" " pour immédiatement entrer votre mot de passe." +" Vous recevrez tous de même un email de confirmation." +" Si vous ne confirmez pas votre adresse dans les délais" +" impartis, votre connexion sera automatiquement suspendue." #: users/forms.py:442 msgid "Send password reset link by email." @@ -961,6 +967,21 @@ msgstr "" "débrancher et rebrancher votre câble Ethernet pour bénéficier d'une " "connexion filaire." +#: users/templates/users/confirm_email.html:35 +#, python-format +msgid "Confirmation email" +msgstr "Email de confirmation" + +#: users/templates/users/confirm_email.html:36 +#, python-format +msgid "Confirm the email" +msgstr "Confirmer l'email" + +#: users/templates/users/confirm_email.html:36 +#, python-format +msgid "for %(name)s." +msgstr "pour %(name)s." + #: users/templates/users/profil.html:36 #, python-format msgid "Welcome %(name)s %(surname)s" @@ -971,6 +992,25 @@ msgstr "Bienvenue %(name)s %(surname)s" msgid "Profile of %(name)s %(surname)s" msgstr "Profil de %(name)s %(surname)s" +#: users/templates/users/profil.html:43 +#, python-format +msgid "" +"Please confirm your email address before %(confirm_before_date)s," +" or your account will be suspended." +msgstr "" +"Veuillez confirmer votre adresse mail avant le %(confirm_before_date)s," +" sous peine de suspension de votre compte." + +#: users/templates/users/profil.html:48 +#, python-format +msgid "Didn't receive the email?" +msgstr "Vous n'avez pas reçu le mail ?" + +#: users/templates/users/profil.html:53 +#, python-format +msgid "Your account has been suspended." +msgstr "Votre compte a été suspendu." + #: users/templates/users/profil.html:46 msgid "Your account has been banned." msgstr "Votre compte a été banni." @@ -1054,6 +1094,10 @@ msgstr "Envoi de mails désactivé" msgid "Connected" msgstr "Connecté" +#: users/templates/users/profil.html:201 +msgid "Pending confirmation..." +msgstr "En attente de confirmation..." + #: users/templates/users/profil.html:193 msgid "Pending connection..." msgstr "Connexion en attente..." @@ -1203,6 +1247,16 @@ msgstr "" msgid "Add an email address" msgstr "Ajouter une adresse mail" +#: users/templates/users/resend_confirmation_email.html:35 +#, python-format +msgid "Re-send confirmation email?" +msgstr "Renvoyer l'email de confirmation ?" + +#: users/templates/users/resend_confirmation_email.html:36 +#, python-format +msgid "The confirmation email will be sent to" +msgstr "L'email de confirmation sera envoyé à" + #: users/templates/users/sidebar.html:33 msgid "Create a club or organisation" msgstr "Créer un club ou une association" @@ -1264,10 +1318,11 @@ msgstr "Adresse MAC %(mac)s" #: users/views.py:131 #, python-format -msgid "The user %s was created." -msgstr "L'utilisateur %s a été créé." +msgid "The user %s was created, a confirmation email was sent." +msgstr "" +"L'utilisateur %s a été créé, un mail de confirmation a été envoyé." -#: users/views.py:127 +#: users/views.py:130 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" @@ -1296,6 +1351,10 @@ msgstr "Le club a été modifié." msgid "The user was edited." msgstr "L'utilisateur a été modifié." +#: users/views.py:229 +msgid "Sent a new confirmation email." +msgstr "Un nouveau mail de confirmation a été envoyé." + #: users/views.py:225 msgid "The state was edited." msgstr "L'état a été modifié." @@ -1477,6 +1536,14 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." +#: users/views.py:1044 +msgid "The %s address was confirmed." +msgstr "L'adresse mail %s a été confirmée." + +#: users/views.py:1068 +msgid "An email to confirm your address was sent." +msgstr "Un mail pour confirmer votre adresse a été envoyé." + #: users/views.py:1072 users/views.py:1096 users/views.py:1111 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 1e52d61a..1b1db462 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -50,7 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% elif users.state == users.STATE_DISABLED %}
- {% blocktrans %}Your account has been disabled{% endblocktrans %} + {% blocktrans %}Your account has been suspended.{% endblocktrans %}
{% endif %} diff --git a/users/views.py b/users/views.py index 19d7d97f..36ddefbd 100644 --- a/users/views.py +++ b/users/views.py @@ -129,7 +129,7 @@ def new_user(request): user.confirm_email_address_mail(request) messages.success( request, - _("The user %s was created.") + _("The user %s was created, a confirmation email was sent.") % user.pseudo, ) else: @@ -226,7 +226,7 @@ def edit_info(request, user, userid): if user_form.should_send_confirmation_email: user.confirm_email_address_mail(request) - messages.success(request, _("Sent a new confirmation email")) + messages.success(request, _("Sent a new confirmation email.")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( From 811e527232b27df80e37e59c725c179fb84c98e3 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 16:32:37 +0200 Subject: [PATCH 058/490] Allow users in the STATE_EMAIL_NOT_YET_CONFIRMED to reset their password --- users/forms.py | 1 + users/views.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/users/forms.py b/users/forms.py index da833203..8135ed30 100644 --- a/users/forms.py +++ b/users/forms.py @@ -113,6 +113,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): """Changement du mot de passe""" user = super(PassForm, self).save(commit=False) user.set_password(self.cleaned_data.get("passwd1")) + user.state = User.STATE_NOT_YET_ACTIVE user.set_active() user.save() diff --git a/users/views.py b/users/views.py index 36ddefbd..0a4d05f5 100644 --- a/users/views.py +++ b/users/views.py @@ -979,7 +979,7 @@ def reset_password(request): user = User.objects.get( pseudo=userform.cleaned_data["pseudo"], email=userform.cleaned_data["email"], - state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE], + state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) From 76ff62f3647744b26a62f97fe442c94dd9d4681a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 16:34:34 +0200 Subject: [PATCH 059/490] Add missing case for STATE_EMAIL_NOT_YET_CONFIRMED state in API --- api/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/api/views.py b/api/views.py index 4077eeeb..58730754 100644 --- a/api/views.py +++ b/api/views.py @@ -513,6 +513,7 @@ class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): queryset = users.User.objects.exclude( Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE) + | Q(state=users.STATE_EMAIL_NOT_YET_CONFIRMED) | Q(state=users.User.STATE_FULL_ARCHIVE) ) serializer_class = serializers.BasicUserSerializer From 3cc2a768dcd4c8e7ae117bdcd4b52ca40c423b22 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 17 Apr 2020 16:40:09 +0200 Subject: [PATCH 060/490] Update resend_confirmation_email.html --- users/templates/users/resend_confirmation_email.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/users/templates/users/resend_confirmation_email.html b/users/templates/users/resend_confirmation_email.html index e0f782e0..ab226374 100644 --- a/users/templates/users/resend_confirmation_email.html +++ b/users/templates/users/resend_confirmation_email.html @@ -4,9 +4,7 @@ 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 Lara Kermarec -Copyright © 2017 Augustin Lemesle +Copyright © 2020 Jean-Romain Garnier 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 From ffdb32df906d0eae155fda79c22b8ee7cb1eabca Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 17 Apr 2020 16:48:27 +0200 Subject: [PATCH 061/490] Update headers --- users/forms.py | 8 +++++--- users/management/commands/disable_emailnotyetconfirmed.py | 4 +--- users/models.py | 8 +++++--- users/templates/users/confirm_email.html | 8 +++++--- users/templates/users/profil.html | 8 +++++--- users/urls.py | 8 +++++--- users/views.py | 8 +++++--- 7 files changed, 31 insertions(+), 21 deletions(-) diff --git a/users/forms.py b/users/forms.py index 8135ed30..ad5c3708 100644 --- a/users/forms.py +++ b/users/forms.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # 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 diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 89dd3e86..955234e5 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -1,6 +1,4 @@ -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Jean-Romain Garnier # # 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 diff --git a/users/models.py b/users/models.py index e64db5d1..397dc7f3 100755 --- a/users/models.py +++ b/users/models.py @@ -3,9 +3,11 @@ # Il se veut agnostique au réseau considéré, de manière à être installable # en quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # 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 diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html index 60bcf11d..b00d3e88 100644 --- a/users/templates/users/confirm_email.html +++ b/users/templates/users/confirm_email.html @@ -4,9 +4,11 @@ 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 Lara Kermarec -Copyright © 2017 Augustin Lemesle +Copyright © 2017-2020 Gabriel Détraz +Copyright © 2017-2020 Lara Kermarec +Copyright © 2017-2020 Augustin Lemesle +Copyright © 2017-2020 Hugo Levy--Falk +Copyright © 2017-2020 Jean-Romain Garnier 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 diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 1b1db462..ed613537 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -4,9 +4,11 @@ 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 Lara Kermarec -Copyright © 2017 Augustin Lemesle +Copyright © 2017-2020 Gabriel Détraz +Copyright © 2017-2020 Lara Kermarec +Copyright © 2017-2020 Augustin Lemesle +Copyright © 2017-2020 Hugo Levy--Falk +Copyright © 2017-2020 Jean-Romain Garnier 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 diff --git a/users/urls.py b/users/urls.py index 8ab5253d..a1579a29 100644 --- a/users/urls.py +++ b/users/urls.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # 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 diff --git a/users/views.py b/users/views.py index 0a4d05f5..fb5f8d73 100644 --- a/users/views.py +++ b/users/views.py @@ -3,9 +3,11 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Lara Kermarec +# Copyright © 2017-2020 Augustin Lemesle +# Copyright © 2017-2020 Hugo Levy--Falk +# Copyright © 2017-2020 Jean-Romain Garnier # # 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 From 0dcd8b79e268e587b9cc69944077691121d3b5a2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 16:51:58 +0200 Subject: [PATCH 062/490] Create STATE_SUSPENDED --- api/views.py | 3 ++- logs/locale/fr/LC_MESSAGES/django.po | 4 ++++ logs/views.py | 10 ++++++++++ preferences/forms.py | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 8 ++++---- ...9_optionaluser_disable_emailnotyetconfirmed.py | 4 ++-- preferences/models.py | 4 ++-- .../preferences/display_preferences.html | 4 ++-- search/forms.py | 1 + search/locale/fr/LC_MESSAGES/django.po | 4 ++++ users/forms.py | 8 ++++++-- users/management/commands/clean_notyetactive.py | 6 +++--- .../commands/disable_emailnotyetconfirmed.py | 14 +++++++------- users/models.py | 15 +++++++++------ ...email_disable_notif => email_suspension_notif} | 0 users/templates/users/profil.html | 2 +- users/views.py | 2 +- 17 files changed, 59 insertions(+), 32 deletions(-) rename users/templates/users/{email_disable_notif => email_suspension_notif} (100%) diff --git a/api/views.py b/api/views.py index 58730754..5dae2bba 100644 --- a/api/views.py +++ b/api/views.py @@ -513,8 +513,9 @@ class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): queryset = users.User.objects.exclude( Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE) - | Q(state=users.STATE_EMAIL_NOT_YET_CONFIRMED) + | Q(state=users.User.STATE_EMAIL_NOT_YET_CONFIRMED) | Q(state=users.User.STATE_FULL_ARCHIVE) + | Q(state=users.User.STATE_SUSPENDED) ) serializer_class = serializers.BasicUserSerializer diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 92dbb89b..6268b05b 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -247,6 +247,10 @@ msgstr "Utilisateurs pas encore actifs" msgid "Waiting for email confirmation users" msgstr "Utilisateurs en attente de confirmation d'email" +#: logs/views.py:273 +msgid "Suspended users" +msgstr "Utilisateurs suspendus" + #: logs/views.py:273 msgid "Contributing members" msgstr "Adhérents cotisants" diff --git a/logs/views.py b/logs/views.py index 615fb69b..69ec5635 100644 --- a/logs/views.py +++ b/logs/views.py @@ -270,6 +270,16 @@ def stats_general(request): ), Club.objects.filter(state=Club.STATE_EMAIL_NOT_YET_CONFIRMED).count(), ], + "suspended_users": [ + _("Suspended users"), + User.objects.filter(state=User.STATE_SUSPENDED).count(), + ( + Adherent.objects.filter( + state=Adherent.STATE_SUSPENDED + ).count() + ), + Club.objects.filter(state=Club.STATE_SUSPENDED).count(), + ], "adherent_users": [ _("Contributing members"), _all_adherent.count(), diff --git a/preferences/forms.py b/preferences/forms.py index b296c0d4..1fdefa6a 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -66,7 +66,7 @@ class EditOptionalUserForm(ModelForm): self.fields["gpg_fingerprint"].label = _("GPG fingerprint") self.fields["all_can_create_club"].label = _("All can create a club") self.fields["all_can_create_adherent"].label = _("All can create a member") - self.fields["disable_emailnotyetconfirmed"].label = _("Delay before disabling accounts without a verified email") + self.fields["suspend_emailnotyetconfirmed"].label = _("Delay before suspending accounts without a verified email") self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index e7ac2e66..caa5719c 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -287,11 +287,11 @@ msgstr "" #: preferences/models.py:111 msgid "" -"Users with an email address not yet confirmed will be disabled after" +"Users with an email address not yet confirmed will be suspended after" " this number of days." msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront" -" désactivés après ce nombre de jours" +" suspendus après ce nombre de jours" #: preferences/models.py:114 msgid "A new user can create their account on Re2o." @@ -1083,8 +1083,8 @@ msgid "Allow directly entering a password during account creation" msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" #: preferences/templates/preferences/display_preferences.html:136 -msgid "Delay before disabling accounts without a verified email" -msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" +msgid "Delay before suspending accounts without a verified email" +msgstr "Délai avant la suspension des comptes sans adresse mail confirmé" #: preferences/templates/preferences/display_preferences.html:136 msgid "Users general permissions" diff --git a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py index 3cc12081..137211ff 100644 --- a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py +++ b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py @@ -14,8 +14,8 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='optionaluser', - name='disable_emailnotyetconfirmed', - field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be disabled after this number of days.') + name='suspend_emailnotyetconfirmed', + field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be suspended after this number of days.') ), ] diff --git a/preferences/models.py b/preferences/models.py index e470303d..360f2e51 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -107,10 +107,10 @@ class OptionalUser(AclMixin, PreferencesModel): "Not yet active users will be deleted after this number of days." ), ) - disable_emailnotyetconfirmed = models.IntegerField( + suspend_emailnotyetconfirmed = models.IntegerField( default=2, help_text=_( - "Users with an email address not yet confirmed will be disabled after this number of days." + "Users with an email address not yet confirmed will be suspended after this number of days." ), ) self_adhesion = models.BooleanField( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 8eb75918..6ccb1b2f 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -131,8 +131,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Allow directly entering a password during account creation" %} {{ useroptions.allow_set_password_during_user_creation|tick }} - {% trans "Delay before disabling accounts without a verified email" %} - {% blocktrans with disable_emailnotyetconfirmed=useroptions.disable_emailnotyetconfirmed %}{{ disable_emailnotyetconfirmed }} days{% endblocktrans %} + {% trans "Delay before suspending accounts without a verified email" %} + {% blocktrans with suspend_emailnotyetconfirmed=useroptions.suspend_emailnotyetconfirmed %}{{ suspend_emailnotyetconfirmed }} days{% endblocktrans %} diff --git a/search/forms.py b/search/forms.py index 741a15e8..a49b92e7 100644 --- a/search/forms.py +++ b/search/forms.py @@ -36,6 +36,7 @@ CHOICES_USER = ( ("3", _("Not yet active")), ("4", _("Fully archived")), ("5", _("Waiting for email confirmation")), + ("6", _("Suspended")), ) CHOICES_AFF = ( diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 6da1a477..4c34f29d 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -54,6 +54,10 @@ msgstr "Complètement archivés" msgid "Waiting for email confirmation" msgstr "En attente de confirmation d'email" +#: search/forms.py:39 +msgid "Suspended" +msgstr "Suspendu" + #: search/forms.py:41 msgid "Users" msgstr "Utilisateurs" diff --git a/users/forms.py b/users/forms.py index ad5c3708..52f4232b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -388,10 +388,14 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): if not self.is_anon and self.initial["email"] and user.email != self.initial["email"]: # Send a confirmation email - if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: - user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED + # Don't do this for archived or disabled users + if user.state not in [User.STATE_ARCHIVE, User.STATE_FULL_ARCHIVE, User.STATE_DISABLED]: self.should_send_confirmation_email = True + # Suspend users stay suspended + if user.state == User.STATE_SUSPENDED: + user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED + # Always keep the oldest change date if user.email_change_date is None: user.email_change_date = timezone.now() diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index d1857096..5ca68827 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -28,13 +28,13 @@ from django.utils import timezone class Command(BaseCommand): - help = "Delete non members users (not yet active or disabled too long ago without an invoice)." + help = "Delete non members users (not yet active or suspended too long ago without an invoice)." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + days = OptionalUser.get_cached_value("delete_notyetactive") users_to_delete = ( - User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_DISABLED)) + User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_SUSPENDED)) .filter(registered__lte=timezone.now() - timedelta(days=days)) .exclude(facture__valid=True) .distinct() diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 955234e5..e0cd6d5e 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -25,19 +25,19 @@ from django.utils import timezone class Command(BaseCommand): - help = "Disable users who haven't confirmed their email." + help = "Suspend users who haven't confirmed their email." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") - users_to_disable = ( + days = OptionalUser.get_cached_value("suspend_emailnotyetconfirmed") + users_to_suspend = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) - print("Disabling " + str(users_to_disable.count()) + " users.") + print("Suspending " + str(users_to_suspend.count()) + " users.") - for user in users_to_disable: - user.state = User.STATE_DISABLED - user.notif_disable() + for user in users_to_suspend: + user.state = User.STATE_SUSPENDED + user.notif_suspension() user.save() diff --git a/users/models.py b/users/models.py index 397dc7f3..36884115 100755 --- a/users/models.py +++ b/users/models.py @@ -174,12 +174,13 @@ class User( Champs principaux : name, surnname, pseudo, email, room, password Herite du django BaseUser et du système d'auth django""" - STATE_ACTIVE = 0 - STATE_DISABLED = 1 + STATE_ACTIVE = 0 # Can login and has Internet (if invoice is valid) + STATE_DISABLED = 1 # Cannot login to Re2o and doesn't have Internet STATE_ARCHIVE = 2 STATE_NOT_YET_ACTIVE = 3 STATE_FULL_ARCHIVE = 4 STATE_EMAIL_NOT_YET_CONFIRMED = 5 + STATE_SUSPENDED = 6 # Can login to Re2o but doesn't have Internet STATES = ( (0, _("Active")), (1, _("Disabled")), @@ -187,6 +188,7 @@ class User( (3, _("Not yet active")), (4, _("Fully archived")), (5, _("Waiting for email confirmation")), + (5, _("Suspended")), ) surname = models.CharField(max_length=255) @@ -395,7 +397,7 @@ class User( @cached_property def get_shadow_expire(self): """Return the shadow_expire value for the user""" - if self.state == self.STATE_DISABLED: + if self.state == self.STATE_DISABLED or self.STATE_SUSPENDED: return str(0) else: return None @@ -690,6 +692,7 @@ class User( or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or self.state == self.STATE_ARCHIVE or self.state == self.STATE_DISABLED + or self.state == self.STATE_SUSPENDED ): self.refresh_from_db() try: @@ -810,7 +813,7 @@ class User( if self.email_change_date is None or self.state != self.STATE_EMAIL_NOT_YET_CONFIRMED: return None - days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + days = OptionalUser.get_cached_value("suspend_emailnotyetconfirmed") return self.email_change_date + timedelta(days=days) def confirm_email_address_mail(self, request): @@ -897,9 +900,9 @@ class User( ) return - def notif_disable(self): + def notif_suspension(self): """Envoi un mail de notification informant que l'adresse mail n'a pas été confirmée""" - template = loader.get_template("users/email_disable_notif") + template = loader.get_template("users/email_suspension_notif") context = { "name": self.get_full_name(), "asso_name": AssoOption.get_cached_value("name"), diff --git a/users/templates/users/email_disable_notif b/users/templates/users/email_suspension_notif similarity index 100% rename from users/templates/users/email_disable_notif rename to users/templates/users/email_suspension_notif diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index ed613537..e6878927 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -50,7 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% blocktrans %}Didn't receive the email?{% endblocktrans %}
-{% elif users.state == users.STATE_DISABLED %} +{% elif users.state == users.STATE_SUSPENDED %}
{% blocktrans %}Your account has been suspended.{% endblocktrans %}
diff --git a/users/views.py b/users/views.py index fb5f8d73..0f7fee24 100644 --- a/users/views.py +++ b/users/views.py @@ -981,7 +981,7 @@ def reset_password(request): user = User.objects.get( pseudo=userform.cleaned_data["pseudo"], email=userform.cleaned_data["email"], - state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED], + state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED, User.STATE_SUSPENDED], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) From bcb22f13822772163924213622512f505ff9aea6 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 16:57:29 +0200 Subject: [PATCH 063/490] Allow suspended users to login --- users/models.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/users/models.py b/users/models.py index 36884115..09877ae1 100755 --- a/users/models.py +++ b/users/models.py @@ -335,6 +335,7 @@ class User( self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED + or self.state == self.STATE_SUSPENDED or ( allow_archived and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE) @@ -344,7 +345,7 @@ class User( def set_active(self): """Enable this user if he subscribed successfully one time before Reenable it if it was archived - Do nothing if disabled or waiting for email confirmation""" + Do nothing if suspended, disabled or waiting for email confirmation""" if self.state == self.STATE_NOT_YET_ACTIVE: if self.facture_set.filter(valid=True).filter( Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion") From 47772f14649cde90c2cbd181dfac17ed08daf0e2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 17:03:51 +0200 Subject: [PATCH 064/490] Revert "Allow suspended users to login" This reverts commit 91c51c50df1d947cb451a4a037cb2b651a973ba0. --- users/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/users/models.py b/users/models.py index 09877ae1..36884115 100755 --- a/users/models.py +++ b/users/models.py @@ -335,7 +335,6 @@ class User( self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED - or self.state == self.STATE_SUSPENDED or ( allow_archived and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE) @@ -345,7 +344,7 @@ class User( def set_active(self): """Enable this user if he subscribed successfully one time before Reenable it if it was archived - Do nothing if suspended, disabled or waiting for email confirmation""" + Do nothing if disabled or waiting for email confirmation""" if self.state == self.STATE_NOT_YET_ACTIVE: if self.facture_set.filter(valid=True).filter( Q(vente__type_cotisation="All") | Q(vente__type_cotisation="Adhesion") From 81b6b999beaf4f18ea9d2fad5bfe4785066041cf Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 17:03:54 +0200 Subject: [PATCH 065/490] Revert "Create STATE_SUSPENDED" This reverts commit 2aef2ae3fae59ef041542be533d2641dc7d6fbd2. --- api/views.py | 3 +-- logs/locale/fr/LC_MESSAGES/django.po | 4 ---- logs/views.py | 10 ---------- preferences/forms.py | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 8 ++++---- ...9_optionaluser_disable_emailnotyetconfirmed.py | 4 ++-- preferences/models.py | 4 ++-- .../preferences/display_preferences.html | 4 ++-- search/forms.py | 1 - search/locale/fr/LC_MESSAGES/django.po | 4 ---- users/forms.py | 8 ++------ users/management/commands/clean_notyetactive.py | 6 +++--- .../commands/disable_emailnotyetconfirmed.py | 14 +++++++------- users/models.py | 15 ++++++--------- ...email_suspension_notif => email_disable_notif} | 0 users/templates/users/profil.html | 2 +- users/views.py | 2 +- 17 files changed, 32 insertions(+), 59 deletions(-) rename users/templates/users/{email_suspension_notif => email_disable_notif} (100%) diff --git a/api/views.py b/api/views.py index 5dae2bba..58730754 100644 --- a/api/views.py +++ b/api/views.py @@ -513,9 +513,8 @@ class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): queryset = users.User.objects.exclude( Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE) - | Q(state=users.User.STATE_EMAIL_NOT_YET_CONFIRMED) + | Q(state=users.STATE_EMAIL_NOT_YET_CONFIRMED) | Q(state=users.User.STATE_FULL_ARCHIVE) - | Q(state=users.User.STATE_SUSPENDED) ) serializer_class = serializers.BasicUserSerializer diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 6268b05b..92dbb89b 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -247,10 +247,6 @@ msgstr "Utilisateurs pas encore actifs" msgid "Waiting for email confirmation users" msgstr "Utilisateurs en attente de confirmation d'email" -#: logs/views.py:273 -msgid "Suspended users" -msgstr "Utilisateurs suspendus" - #: logs/views.py:273 msgid "Contributing members" msgstr "Adhérents cotisants" diff --git a/logs/views.py b/logs/views.py index 69ec5635..615fb69b 100644 --- a/logs/views.py +++ b/logs/views.py @@ -270,16 +270,6 @@ def stats_general(request): ), Club.objects.filter(state=Club.STATE_EMAIL_NOT_YET_CONFIRMED).count(), ], - "suspended_users": [ - _("Suspended users"), - User.objects.filter(state=User.STATE_SUSPENDED).count(), - ( - Adherent.objects.filter( - state=Adherent.STATE_SUSPENDED - ).count() - ), - Club.objects.filter(state=Club.STATE_SUSPENDED).count(), - ], "adherent_users": [ _("Contributing members"), _all_adherent.count(), diff --git a/preferences/forms.py b/preferences/forms.py index 1fdefa6a..b296c0d4 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -66,7 +66,7 @@ class EditOptionalUserForm(ModelForm): self.fields["gpg_fingerprint"].label = _("GPG fingerprint") self.fields["all_can_create_club"].label = _("All can create a club") self.fields["all_can_create_adherent"].label = _("All can create a member") - self.fields["suspend_emailnotyetconfirmed"].label = _("Delay before suspending accounts without a verified email") + self.fields["disable_emailnotyetconfirmed"].label = _("Delay before disabling accounts without a verified email") self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index caa5719c..e7ac2e66 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -287,11 +287,11 @@ msgstr "" #: preferences/models.py:111 msgid "" -"Users with an email address not yet confirmed will be suspended after" +"Users with an email address not yet confirmed will be disabled after" " this number of days." msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront" -" suspendus après ce nombre de jours" +" désactivés après ce nombre de jours" #: preferences/models.py:114 msgid "A new user can create their account on Re2o." @@ -1083,8 +1083,8 @@ msgid "Allow directly entering a password during account creation" msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" #: preferences/templates/preferences/display_preferences.html:136 -msgid "Delay before suspending accounts without a verified email" -msgstr "Délai avant la suspension des comptes sans adresse mail confirmé" +msgid "Delay before disabling accounts without a verified email" +msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" #: preferences/templates/preferences/display_preferences.html:136 msgid "Users general permissions" diff --git a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py index 137211ff..3cc12081 100644 --- a/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py +++ b/preferences/migrations/0069_optionaluser_disable_emailnotyetconfirmed.py @@ -14,8 +14,8 @@ class Migration(migrations.Migration): operations = [ migrations.AddField( model_name='optionaluser', - name='suspend_emailnotyetconfirmed', - field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be suspended after this number of days.') + name='disable_emailnotyetconfirmed', + field=models.IntegerField(default=2, help_text='Users with an email address not yet confirmed will be disabled after this number of days.') ), ] diff --git a/preferences/models.py b/preferences/models.py index 360f2e51..e470303d 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -107,10 +107,10 @@ class OptionalUser(AclMixin, PreferencesModel): "Not yet active users will be deleted after this number of days." ), ) - suspend_emailnotyetconfirmed = models.IntegerField( + disable_emailnotyetconfirmed = models.IntegerField( default=2, help_text=_( - "Users with an email address not yet confirmed will be suspended after this number of days." + "Users with an email address not yet confirmed will be disabled after this number of days." ), ) self_adhesion = models.BooleanField( diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 6ccb1b2f..8eb75918 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -131,8 +131,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Allow directly entering a password during account creation" %} {{ useroptions.allow_set_password_during_user_creation|tick }} - {% trans "Delay before suspending accounts without a verified email" %} - {% blocktrans with suspend_emailnotyetconfirmed=useroptions.suspend_emailnotyetconfirmed %}{{ suspend_emailnotyetconfirmed }} days{% endblocktrans %} + {% trans "Delay before disabling accounts without a verified email" %} + {% blocktrans with disable_emailnotyetconfirmed=useroptions.disable_emailnotyetconfirmed %}{{ disable_emailnotyetconfirmed }} days{% endblocktrans %} diff --git a/search/forms.py b/search/forms.py index a49b92e7..741a15e8 100644 --- a/search/forms.py +++ b/search/forms.py @@ -36,7 +36,6 @@ CHOICES_USER = ( ("3", _("Not yet active")), ("4", _("Fully archived")), ("5", _("Waiting for email confirmation")), - ("6", _("Suspended")), ) CHOICES_AFF = ( diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 4c34f29d..6da1a477 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -54,10 +54,6 @@ msgstr "Complètement archivés" msgid "Waiting for email confirmation" msgstr "En attente de confirmation d'email" -#: search/forms.py:39 -msgid "Suspended" -msgstr "Suspendu" - #: search/forms.py:41 msgid "Users" msgstr "Utilisateurs" diff --git a/users/forms.py b/users/forms.py index 52f4232b..ad5c3708 100644 --- a/users/forms.py +++ b/users/forms.py @@ -388,14 +388,10 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): if not self.is_anon and self.initial["email"] and user.email != self.initial["email"]: # Send a confirmation email - # Don't do this for archived or disabled users - if user.state not in [User.STATE_ARCHIVE, User.STATE_FULL_ARCHIVE, User.STATE_DISABLED]: + if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: + user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED self.should_send_confirmation_email = True - # Suspend users stay suspended - if user.state == User.STATE_SUSPENDED: - user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED - # Always keep the oldest change date if user.email_change_date is None: user.email_change_date = timezone.now() diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index 5ca68827..d1857096 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -28,13 +28,13 @@ from django.utils import timezone class Command(BaseCommand): - help = "Delete non members users (not yet active or suspended too long ago without an invoice)." + help = "Delete non members users (not yet active or disabled too long ago without an invoice)." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("delete_notyetactive") + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_delete = ( - User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_SUSPENDED)) + User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_DISABLED)) .filter(registered__lte=timezone.now() - timedelta(days=days)) .exclude(facture__valid=True) .distinct() diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index e0cd6d5e..955234e5 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -25,19 +25,19 @@ from django.utils import timezone class Command(BaseCommand): - help = "Suspend users who haven't confirmed their email." + help = "Disable users who haven't confirmed their email." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" - days = OptionalUser.get_cached_value("suspend_emailnotyetconfirmed") - users_to_suspend = ( + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") + users_to_disable = ( User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) - print("Suspending " + str(users_to_suspend.count()) + " users.") + print("Disabling " + str(users_to_disable.count()) + " users.") - for user in users_to_suspend: - user.state = User.STATE_SUSPENDED - user.notif_suspension() + for user in users_to_disable: + user.state = User.STATE_DISABLED + user.notif_disable() user.save() diff --git a/users/models.py b/users/models.py index 36884115..397dc7f3 100755 --- a/users/models.py +++ b/users/models.py @@ -174,13 +174,12 @@ class User( Champs principaux : name, surnname, pseudo, email, room, password Herite du django BaseUser et du système d'auth django""" - STATE_ACTIVE = 0 # Can login and has Internet (if invoice is valid) - STATE_DISABLED = 1 # Cannot login to Re2o and doesn't have Internet + STATE_ACTIVE = 0 + STATE_DISABLED = 1 STATE_ARCHIVE = 2 STATE_NOT_YET_ACTIVE = 3 STATE_FULL_ARCHIVE = 4 STATE_EMAIL_NOT_YET_CONFIRMED = 5 - STATE_SUSPENDED = 6 # Can login to Re2o but doesn't have Internet STATES = ( (0, _("Active")), (1, _("Disabled")), @@ -188,7 +187,6 @@ class User( (3, _("Not yet active")), (4, _("Fully archived")), (5, _("Waiting for email confirmation")), - (5, _("Suspended")), ) surname = models.CharField(max_length=255) @@ -397,7 +395,7 @@ class User( @cached_property def get_shadow_expire(self): """Return the shadow_expire value for the user""" - if self.state == self.STATE_DISABLED or self.STATE_SUSPENDED: + if self.state == self.STATE_DISABLED: return str(0) else: return None @@ -692,7 +690,6 @@ class User( or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or self.state == self.STATE_ARCHIVE or self.state == self.STATE_DISABLED - or self.state == self.STATE_SUSPENDED ): self.refresh_from_db() try: @@ -813,7 +810,7 @@ class User( if self.email_change_date is None or self.state != self.STATE_EMAIL_NOT_YET_CONFIRMED: return None - days = OptionalUser.get_cached_value("suspend_emailnotyetconfirmed") + days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") return self.email_change_date + timedelta(days=days) def confirm_email_address_mail(self, request): @@ -900,9 +897,9 @@ class User( ) return - def notif_suspension(self): + def notif_disable(self): """Envoi un mail de notification informant que l'adresse mail n'a pas été confirmée""" - template = loader.get_template("users/email_suspension_notif") + template = loader.get_template("users/email_disable_notif") context = { "name": self.get_full_name(), "asso_name": AssoOption.get_cached_value("name"), diff --git a/users/templates/users/email_suspension_notif b/users/templates/users/email_disable_notif similarity index 100% rename from users/templates/users/email_suspension_notif rename to users/templates/users/email_disable_notif diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index e6878927..ed613537 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -50,7 +50,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% blocktrans %}Didn't receive the email?{% endblocktrans %}
-{% elif users.state == users.STATE_SUSPENDED %} +{% elif users.state == users.STATE_DISABLED %}
{% blocktrans %}Your account has been suspended.{% endblocktrans %}
diff --git a/users/views.py b/users/views.py index 0f7fee24..fb5f8d73 100644 --- a/users/views.py +++ b/users/views.py @@ -981,7 +981,7 @@ def reset_password(request): user = User.objects.get( pseudo=userform.cleaned_data["pseudo"], email=userform.cleaned_data["email"], - state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED, User.STATE_SUSPENDED], + state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) From 4803417bcab9af90b3e678916e2b562ef55ae615 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 17:35:24 +0200 Subject: [PATCH 066/490] Replace STATE_EMAIL_NOT_YET_CONFIRMED with an email_state --- api/views.py | 1 - freeradius_utils/auth.py | 17 ++++++++- logs/views.py | 10 ------ re2o/utils.py | 3 +- users/forms.py | 5 ++- users/locale/fr/LC_MESSAGES/django.po | 4 +-- users/management/commands/archive.py | 1 - .../commands/disable_emailnotyetconfirmed.py | 4 +-- users/migrations/0085_auto_20200417_0031.py | 12 +++++-- users/migrations/0085_user_email_state.py | 20 +++++++++++ .../migrations/0086_user_email_change_date.py | 2 +- users/models.py | 36 ++++++++----------- users/templates/users/profil.html | 6 ++-- users/views.py | 5 ++- 14 files changed, 73 insertions(+), 53 deletions(-) create mode 100644 users/migrations/0085_user_email_state.py diff --git a/api/views.py b/api/views.py index 58730754..4077eeeb 100644 --- a/api/views.py +++ b/api/views.py @@ -513,7 +513,6 @@ class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): queryset = users.User.objects.exclude( Q(state=users.User.STATE_DISABLED) | Q(state=users.User.STATE_NOT_YET_ACTIVE) - | Q(state=users.STATE_EMAIL_NOT_YET_CONFIRMED) | Q(state=users.User.STATE_FULL_ARCHIVE) ) serializer_class = serializers.BasicUserSerializer diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index daebf95d..966663b1 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -469,7 +469,7 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): RadiusOption.get_attributes("non_member_attributes", attributes_kwargs), ) for user in room_user: - if user.is_ban() or user.state not in [User.STATE_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: + if user.is_ban() or user.state != User.STATE_ACTIVE: return ( sw_name, room, @@ -480,6 +480,21 @@ def decide_vlan_switch(nas_machine, nas_type, port_number, mac_address): RadiusOption.get_cached_value("banned") != RadiusOption.REJECT, RadiusOption.get_attributes("banned_attributes", attributes_kwargs), ) + elif user.email_state == User.EMAIL_STATE_UNVERIFIED: + return ( + sw_name, + room, + u"Utilisateur suspendu (mail non confirme)", + getattr( + RadiusOption.get_cached_value("non_member_vlan"), + "vlan_id", + None, + ), + RadiusOption.get_cached_value("non_member") != RadiusOption.REJECT, + RadiusOption.get_attributes( + "non_member_attributes", attributes_kwargs + ), + ) elif not (user.is_connected() or user.is_whitelisted()): return ( sw_name, diff --git a/logs/views.py b/logs/views.py index 615fb69b..7c509134 100644 --- a/logs/views.py +++ b/logs/views.py @@ -260,16 +260,6 @@ def stats_general(request): ), Club.objects.filter(state=Club.STATE_NOT_YET_ACTIVE).count(), ], - "email_not_confirmed_users": [ - _("Waiting for email confirmation users"), - User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED).count(), - ( - Adherent.objects.filter( - state=Adherent.STATE_EMAIL_NOT_YET_CONFIRMED - ).count() - ), - Club.objects.filter(state=Club.STATE_EMAIL_NOT_YET_CONFIRMED).count(), - ], "adherent_users": [ _("Contributing members"), _all_adherent.count(), diff --git a/re2o/utils.py b/re2o/utils.py index 61c45c0c..348e5c55 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -116,7 +116,8 @@ def all_has_access(search_time=None, including_asso=True): if search_time is None: search_time = timezone.now() filter_user = ( - (Q(state=User.STATE_ACTIVE) | Q(state=User.STATE_EMAIL_NOT_YET_CONFIRMED)) + Q(state=User.STATE_ACTIVE) + & ~Q(email_state=User.EMAIL_STATE_UNVERIFIED) & ~Q( ban__in=Ban.objects.filter( Q(date_start__lt=search_time) & Q(date_end__gt=search_time) diff --git a/users/forms.py b/users/forms.py index ad5c3708..94002eeb 100644 --- a/users/forms.py +++ b/users/forms.py @@ -388,8 +388,8 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): if not self.is_anon and self.initial["email"] and user.email != self.initial["email"]: # Send a confirmation email - if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED]: - user.state = User.STATE_EMAIL_NOT_YET_CONFIRMED + if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE]: + user.email_state = User.EMAIL_STATE_PENDING self.should_send_confirmation_email = True # Always keep the oldest change date @@ -682,7 +682,6 @@ class StateForm(FormRevMixin, ModelForm): user.state = self.cleaned_data.get("state") user.state_sync() - user.email_change_date_sync() user.save() diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 1d6ec63b..922c2dbe 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -1008,8 +1008,8 @@ msgstr "Vous n'avez pas reçu le mail ?" #: users/templates/users/profil.html:53 #, python-format -msgid "Your account has been suspended." -msgstr "Votre compte a été suspendu." +msgid "Your account has been suspended, please confirm your email address." +msgstr "Votre compte a été suspendu, veuillez confirmer votre adresse mail." #: users/templates/users/profil.html:46 msgid "Your account has been banned." diff --git a/users/management/commands/archive.py b/users/management/commands/archive.py index d730cfcd..1e4601a0 100644 --- a/users/management/commands/archive.py +++ b/users/management/commands/archive.py @@ -77,7 +77,6 @@ class Command(BaseCommand): .exclude(id__in=all_has_access(search_time=date)) .exclude(state=User.STATE_NOT_YET_ACTIVE) .exclude(state=User.STATE_FULL_ARCHIVE) - .exclude(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) ) if show: diff --git a/users/management/commands/disable_emailnotyetconfirmed.py b/users/management/commands/disable_emailnotyetconfirmed.py index 955234e5..85b12699 100644 --- a/users/management/commands/disable_emailnotyetconfirmed.py +++ b/users/management/commands/disable_emailnotyetconfirmed.py @@ -31,13 +31,13 @@ class Command(BaseCommand): """First deleting invalid invoices, and then deleting the users""" days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_disable = ( - User.objects.filter(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) + User.objects.filter(email_state=User.EMAIL_STATE_PENDING) .filter(email_change_date__lte=timezone.now() - timedelta(days=days)) .distinct() ) print("Disabling " + str(users_to_disable.count()) + " users.") for user in users_to_disable: - user.state = User.STATE_DISABLED + user.email_state = User.EMAIL_STATE_UNVERIFIED user.notif_disable() user.save() diff --git a/users/migrations/0085_auto_20200417_0031.py b/users/migrations/0085_auto_20200417_0031.py index 3a2e4241..802ea721 100644 --- a/users/migrations/0085_auto_20200417_0031.py +++ b/users/migrations/0085_auto_20200417_0031.py @@ -11,10 +11,16 @@ class Migration(migrations.Migration): ('users', '0084_auto_20191120_0159'), ] + def flag_verified(apps, schema_editor): + db_alias = schema_editor.connection.alias + users = apps.get_model("users", "User") + users.objects.using(db_alias).all().update(email_state=0) + operations = [ - migrations.AlterField( + migrations.AddField( model_name='user', - name='state', - field=models.IntegerField(choices=[(0, 'Active'), (1, 'Disabled'), (2, 'Archived'), (3, 'Not yet active'), (4, 'Fully archived'), (5, 'Waiting for email confirmation')], default=3), + name='email_state', + field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), ), + migrations.RunPython(flag_verified), ] diff --git a/users/migrations/0085_user_email_state.py b/users/migrations/0085_user_email_state.py new file mode 100644 index 00000000..9dfff9af --- /dev/null +++ b/users/migrations/0085_user_email_state.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-16 22:31 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0084_auto_20191120_0159'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='email_state', + field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), + ), + ] diff --git a/users/migrations/0086_user_email_change_date.py b/users/migrations/0086_user_email_change_date.py index b6f7075c..e8d9ea9d 100644 --- a/users/migrations/0086_user_email_change_date.py +++ b/users/migrations/0086_user_email_change_date.py @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('users', '0085_auto_20200417_0031'), + ('users', '0085_user_email_state'), ] operations = [ diff --git a/users/models.py b/users/models.py index 397dc7f3..b658e9ef 100755 --- a/users/models.py +++ b/users/models.py @@ -179,14 +179,21 @@ class User( STATE_ARCHIVE = 2 STATE_NOT_YET_ACTIVE = 3 STATE_FULL_ARCHIVE = 4 - STATE_EMAIL_NOT_YET_CONFIRMED = 5 STATES = ( (0, _("Active")), (1, _("Disabled")), (2, _("Archived")), (3, _("Not yet active")), (4, _("Fully archived")), - (5, _("Waiting for email confirmation")), + ) + + EMAIL_STATE_VERIFIED = 0 + EMAIL_STATE_UNVERIFIED = 1 + EMAIL_STATE_PENDING = 2 + EMAIL_STATES = ( + (0, _("Verified")), + (1, _("Unverified")), + (2, _("Waiting for email confirmation")), ) surname = models.CharField(max_length=255) @@ -222,6 +229,7 @@ class User( ) pwd_ntlm = models.CharField(max_length=255) state = models.IntegerField(choices=STATES, default=STATE_NOT_YET_ACTIVE) + email_state = models.IntegerField(choices=EMAIL_STATES, default=EMAIL_STATE_PENDING) registered = models.DateTimeField(auto_now_add=True) telephone = models.CharField(max_length=15, blank=True, null=True) uid_number = models.PositiveIntegerField(default=get_fresh_user_uid, unique=True) @@ -332,7 +340,6 @@ class User( return ( self.state == self.STATE_ACTIVE or self.state == self.STATE_NOT_YET_ACTIVE - or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or ( allow_archived and self.state in (self.STATE_ARCHIVE, self.STATE_FULL_ARCHIVE) @@ -395,7 +402,7 @@ class User( @cached_property def get_shadow_expire(self): """Return the shadow_expire value for the user""" - if self.state == self.STATE_DISABLED: + if self.state == self.STATE_DISABLED or self.email_state == self.EMAIL_STATE_UNVERIFIED: return str(0) else: return None @@ -487,7 +494,8 @@ class User( def has_access(self): """ Renvoie si un utilisateur a accès à internet """ return ( - self.state in [User.STATE_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED] + self.state == User.STATE_ACTIVE + and self.email_state != User.EMAIL_STATE_UNVERIFIED and not self.is_ban() and (self.is_connected() or self.is_whitelisted()) ) or self == AssoOption.get_cached_value("utilisateur_asso") @@ -658,21 +666,6 @@ class User( ): self.full_archive() - def email_change_date_sync(self): - """Update user's email_change_date based on state update""" - if ( - self.__original_state != self.STATE_ACTIVE - and self.state == self.STATE_ACTIVE - ): - self.email_change_date = None - self.save() - elif ( - self.__original_state != self.STATE_EMAIL_NOT_YET_CONFIRMED - and self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED - ): - self.email_change_date = timezone.now() - self.save() - def ldap_sync( self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False ): @@ -687,7 +680,6 @@ class User( Si l'instance n'existe pas, on crée le ldapuser correspondant""" if sys.version_info[0] >= 3 and ( self.state == self.STATE_ACTIVE - or self.state == self.STATE_EMAIL_NOT_YET_CONFIRMED or self.state == self.STATE_ARCHIVE or self.state == self.STATE_DISABLED ): @@ -807,7 +799,7 @@ class User( return def confirm_email_before_date(self): - if self.email_change_date is None or self.state != self.STATE_EMAIL_NOT_YET_CONFIRMED: + if self.email_change_date is None or self.email_state == self.EMAIL_STATE_VERIFIED: return None days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index ed613537..c5347dcb 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
-{% if users.state == users.STATE_EMAIL_NOT_YET_CONFIRMED %} +{% if users.email_state == users.EMAIL_STATE_PENDING %}
{% blocktrans with confirm_before_date=users.confirm_email_before_date|date:"DATE_FORMAT" %}Please confirm your email address before {{ confirm_before_date }}, or your account will be suspended.{% endblocktrans %}
@@ -50,9 +50,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% blocktrans %}Didn't receive the email?{% endblocktrans %}
-{% elif users.state == users.STATE_DISABLED %} +{% elif users.email_state == users.EMAIL_STATE_UNVERIFIED %}
- {% blocktrans %}Your account has been suspended.{% endblocktrans %} + {% blocktrans %}Your account has been suspended, please confirm your email address.{% endblocktrans %}
{% endif %} diff --git a/users/views.py b/users/views.py index fb5f8d73..db41c7d1 100644 --- a/users/views.py +++ b/users/views.py @@ -745,7 +745,6 @@ def mass_archive(request): .exclude(id__in=all_has_access(search_time=date)) .exclude(state=User.STATE_NOT_YET_ACTIVE) .exclude(state=User.STATE_FULL_ARCHIVE) - .exclude(state=User.STATE_EMAIL_NOT_YET_CONFIRMED) ) if not full_archive: to_archive_list = to_archive_list.exclude(state=User.STATE_ARCHIVE) @@ -981,7 +980,7 @@ def reset_password(request): user = User.objects.get( pseudo=userform.cleaned_data["pseudo"], email=userform.cleaned_data["email"], - state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE, User.STATE_EMAIL_NOT_YET_CONFIRMED], + state__in=[User.STATE_ACTIVE, User.STATE_NOT_YET_ACTIVE], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) @@ -1060,7 +1059,7 @@ def resend_confirmation_email(request, logged_user, userid): try: user = User.objects.get( id=userid, - state__in=[User.STATE_EMAIL_NOT_YET_CONFIRMED], + email_state__in=[User.EMAIL_STATE_PENDING, User.EMAIL_STATE_UNVERIFIED], ) except User.DoesNotExist: messages.error(request, _("The user doesn't exist.")) From 377f584628db9be1ddcde73741b8da49fdee1b31 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 17:37:47 +0200 Subject: [PATCH 067/490] Delete old migration --- users/migrations/0085_auto_20200417_0031.py | 26 --------------------- users/migrations/0085_user_email_state.py | 6 +++++ 2 files changed, 6 insertions(+), 26 deletions(-) delete mode 100644 users/migrations/0085_auto_20200417_0031.py diff --git a/users/migrations/0085_auto_20200417_0031.py b/users/migrations/0085_auto_20200417_0031.py deleted file mode 100644 index 802ea721..00000000 --- a/users/migrations/0085_auto_20200417_0031.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.28 on 2020-04-16 22:31 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0084_auto_20191120_0159'), - ] - - def flag_verified(apps, schema_editor): - db_alias = schema_editor.connection.alias - users = apps.get_model("users", "User") - users.objects.using(db_alias).all().update(email_state=0) - - operations = [ - migrations.AddField( - model_name='user', - name='email_state', - field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), - ), - migrations.RunPython(flag_verified), - ] diff --git a/users/migrations/0085_user_email_state.py b/users/migrations/0085_user_email_state.py index 9dfff9af..802ea721 100644 --- a/users/migrations/0085_user_email_state.py +++ b/users/migrations/0085_user_email_state.py @@ -11,10 +11,16 @@ class Migration(migrations.Migration): ('users', '0084_auto_20191120_0159'), ] + def flag_verified(apps, schema_editor): + db_alias = schema_editor.connection.alias + users = apps.get_model("users", "User") + users.objects.using(db_alias).all().update(email_state=0) + operations = [ migrations.AddField( model_name='user', name='email_state', field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), ), + migrations.RunPython(flag_verified), ] From 19b463bc1ee45a4d3875e6d3474b5d96d1c14b5f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 17 Apr 2020 17:42:23 +0200 Subject: [PATCH 068/490] Allow revert migrations --- users/migrations/0085_user_email_state.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/users/migrations/0085_user_email_state.py b/users/migrations/0085_user_email_state.py index 802ea721..92535565 100644 --- a/users/migrations/0085_user_email_state.py +++ b/users/migrations/0085_user_email_state.py @@ -16,11 +16,14 @@ class Migration(migrations.Migration): users = apps.get_model("users", "User") users.objects.using(db_alias).all().update(email_state=0) + def undo_flag_verified(apps, schema_editor): + return + operations = [ migrations.AddField( model_name='user', name='email_state', field=models.IntegerField(choices=[(0, 'Verified'), (1, 'Unverified'), (2, 'Waiting for email confirmation')], default=2), ), - migrations.RunPython(flag_verified), + migrations.RunPython(flag_verified, undo_flag_verified), ] From 2a068a7e0a0d98749b8b8c58122d80c844064d2c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:01:35 +0200 Subject: [PATCH 069/490] Fix error preventing migration --- users/forms.py | 71 +++++++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 33 deletions(-) diff --git a/users/forms.py b/users/forms.py index 94002eeb..a29c1fbe 100644 --- a/users/forms.py +++ b/users/forms.py @@ -405,41 +405,40 @@ class AdherentCreationForm(AdherentForm): AdherentForm auquel on ajoute une checkbox afin d'éviter les doublons d'utilisateurs et, optionnellement, un champ mot de passe""" - if OptionalUser.get_cached_value("allow_set_password_during_user_creation"): - # Champ pour choisir si un lien est envoyé par mail pour le mot de passe - init_password_by_mail_info = _( - "If this options is set, you will receive a link to set" - " your initial password by email. If you do not have" - " any means of accessing your emails, you can disable" - " this option to set your password immediatly." - " You will still receive an email to confirm your address." - " Failure to confirm your address will result in an" - " automatic suspension of your account until you do." - ) + # Champ pour choisir si un lien est envoyé par mail pour le mot de passe + init_password_by_mail_info = _( + "If this options is set, you will receive a link to set" + " your initial password by email. If you do not have" + " any means of accessing your emails, you can disable" + " this option to set your password immediatly." + " You will still receive an email to confirm your address." + " Failure to confirm your address will result in an" + " automatic suspension of your account until you do." + ) - init_password_by_mail = forms.BooleanField( - help_text=init_password_by_mail_info, - required=False, - initial=True - ) - init_password_by_mail.label = _("Send password reset link by email.") + init_password_by_mail = forms.BooleanField( + help_text=init_password_by_mail_info, + required=False, + initial=True + ) + init_password_by_mail.label = _("Send password reset link by email.") - # Champs pour initialiser le mot de passe - # Validators are handled manually since theses fields aren't always required - password1 = forms.CharField( - required=False, - label=_("Password"), - widget=forms.PasswordInput, - #validators=[MinLengthValidator(8)], - max_length=255, - ) - password2 = forms.CharField( - required=False, - label=_("Password confirmation"), - widget=forms.PasswordInput, - #validators=[MinLengthValidator(8)], - max_length=255, - ) + # Champs pour initialiser le mot de passe + # Validators are handled manually since theses fields aren't always required + password1 = forms.CharField( + required=False, + label=_("Password"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) + password2 = forms.CharField( + required=False, + label=_("Password confirmation"), + widget=forms.PasswordInput, + #validators=[MinLengthValidator(8)], + max_length=255, + ) # Champ permettant d'éviter au maxium les doublons d'utilisateurs former_user_check_info = _( @@ -481,6 +480,12 @@ class AdherentCreationForm(AdherentForm): ) ) + # Remove password fields if option is disabled + if not OptionalUser.get_cached_value("allow_set_password_during_user_creation"): + self.fields.pop("init_password_by_mail") + self.fields.pop("password1") + self.fields.pop("password2") + def clean_password1(self): """Ignore ce champs si la case init_password_by_mail est décochée""" send_email = self.cleaned_data.get("init_password_by_mail") From ba7fe6e035b95fe666aadfc9f8bf2708db281de8 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:16:21 +0200 Subject: [PATCH 070/490] Automatically validate superuer's email address --- users/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/users/models.py b/users/models.py index b658e9ef..f6b9387e 100755 --- a/users/models.py +++ b/users/models.py @@ -147,6 +147,8 @@ class UserManager(BaseUserManager): ) user.set_password(password) + user.email_change_date = None + user.email_state = User.EMAIL_STATE_VERIFIED if su: user.is_superuser = True user.save(using=self._db) From 73b11a3e93596778fb23a95e4df8b3fe30f1c46c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:16:53 +0200 Subject: [PATCH 071/490] Make check of pending email confirmation cleaner in profile.html --- users/templates/users/profil.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index c5347dcb..82feb8e6 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -200,7 +200,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Email address" %}
-
{{ users.email }}{% if users.email_change_date is not None %}
{% trans "Pending confirmation..." %}{% endif %}
+
{{ users.email }}{% if users.email_state != users.EMAIL_STATE_VERIFIED %}
{% trans "Pending confirmation..." %}{% endif %}
From d8cfb2e3d523726af7e5f923afda939087f73206 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:29:22 +0200 Subject: [PATCH 072/490] Set email_change_date on user creation --- users/forms.py | 1 + 1 file changed, 1 insertion(+) diff --git a/users/forms.py b/users/forms.py index a29c1fbe..d7c3c15b 100644 --- a/users/forms.py +++ b/users/forms.py @@ -518,6 +518,7 @@ class AdherentCreationForm(AdherentForm): an email to init the password should be sent""" # Save the provided password in hashed format user = super(AdherentForm, self).save(commit=False) + user.email_change_date = timezone.now() is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") send_email = not is_set_password_allowed or self.cleaned_data.get("init_password_by_mail") From 8e543a04b1e21277f3963b780490475a1e2c3e0b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:32:38 +0200 Subject: [PATCH 073/490] Fix marking email as verified --- users/models.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/users/models.py b/users/models.py index f6b9387e..9c43561f 100755 --- a/users/models.py +++ b/users/models.py @@ -920,12 +920,9 @@ class User( def confirm_mail(self): """Marque l'email de l'utilisateur comme confirmé""" - # Reset the email change date + # Reset the email change date and update the email status self.email_change_date = None - - # Let the "set_active" method handle the rest - self.state = self.STATE_NOT_YET_ACTIVE - self.set_active() + self.email_state = self.EMAIL_STATE_VERIFIED @cached_property def email_address(self): From 43cdd54db90646bfc4e9be863b9ddc98c4913142 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:46:18 +0200 Subject: [PATCH 074/490] Remove obsolete user state option from search --- search/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/search/forms.py b/search/forms.py index 741a15e8..9f2ff82a 100644 --- a/search/forms.py +++ b/search/forms.py @@ -35,7 +35,6 @@ CHOICES_USER = ( ("2", _("Archived")), ("3", _("Not yet active")), ("4", _("Fully archived")), - ("5", _("Waiting for email confirmation")), ) CHOICES_AFF = ( From fcd2f9d16d9c0250363af0b3192792657b183767 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:53:49 +0200 Subject: [PATCH 075/490] Also update email state in EmailSettingsForm --- users/forms.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/users/forms.py b/users/forms.py index d7c3c15b..6fda0847 100644 --- a/users/forms.py +++ b/users/forms.py @@ -875,6 +875,22 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): ) ) + def save(self, commit=True): + """Update email state if email was changed""" + user = super(EmailSettingsForm, self).save(commit=commit) + + if self.initial["email"] and user.email != self.initial["email"]: + # Send a confirmation email + if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE]: + user.email_state = User.EMAIL_STATE_PENDING + self.should_send_confirmation_email = True + + # Always keep the oldest change date + if user.email_change_date is None: + user.email_change_date = timezone.now() + + user.save() + class Meta: model = User fields = ["email", "local_email_enabled", "local_email_redirect"] From cdc25ad5ec16b330b698d10135f36531d015d5e9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 18:59:21 +0200 Subject: [PATCH 076/490] Send confirmation email if necessary after editing EmailSettingsForm --- users/forms.py | 3 +++ users/views.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/users/forms.py b/users/forms.py index 6fda0847..0e40870e 100644 --- a/users/forms.py +++ b/users/forms.py @@ -857,7 +857,10 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) + + self.should_send_confirmation_email = False self.fields["email"].label = _("Main email address") + if "local_email_redirect" in self.fields: self.fields["local_email_redirect"].label = _("Redirect local emails") if "local_email_enabled" in self.fields: diff --git a/users/views.py b/users/views.py index db41c7d1..027896ed 100644 --- a/users/views.py +++ b/users/views.py @@ -546,6 +546,12 @@ def edit_email_settings(request, user_instance, **_kwargs): if email_settings.changed_data: email_settings.save() messages.success(request, _("The email settings were edited.")) + + # Send confirmation email if necessary + if email_settings.should_send_confirmation_email is True: + user_instance.confirm_email_address_mail(request) + messages.success(request, _("An email to confirm your address was sent.")) + return redirect( reverse("users:profil", kwargs={"userid": str(user_instance.id)}) ) From ac7ea6cd239ddde4ddaa1442aaf49a086d72cd3e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 17:04:00 +0000 Subject: [PATCH 077/490] Move should_send_confirmation_email in EmailSettingsForm for consistency --- users/forms.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/users/forms.py b/users/forms.py index 0e40870e..68492937 100644 --- a/users/forms.py +++ b/users/forms.py @@ -853,14 +853,12 @@ class EMailAddressForm(FormRevMixin, ModelForm): class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Edit email-related settings""" + should_send_confirmation_email = False def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) - - self.should_send_confirmation_email = False self.fields["email"].label = _("Main email address") - if "local_email_redirect" in self.fields: self.fields["local_email_redirect"].label = _("Redirect local emails") if "local_email_enabled" in self.fields: From 6d0c53d667752fcb8cfbb31177c4a9b76e0e564c Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 17 Apr 2020 19:18:11 +0200 Subject: [PATCH 078/490] =?UTF-8?q?Remove=20state=5Fsync=20:=20est=20appel?= =?UTF-8?q?l=C3=A9=20en=20post-save=20de=20user?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- users/forms.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/users/forms.py b/users/forms.py index 68492937..441d645c 100644 --- a/users/forms.py +++ b/users/forms.py @@ -682,14 +682,6 @@ class StateForm(FormRevMixin, ModelForm): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) - def save(self, commit=True): - user = super(StateForm, self).save(commit=False) - if self.cleaned_data["state"]: - user.state = self.cleaned_data.get("state") - user.state_sync() - - user.save() - class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): """ Gestion des groupes d'un user""" From 8b4e2037833549ee2f21ae37dfb284a3b68f0b4c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 19:11:44 +0200 Subject: [PATCH 079/490] Reset clean_notyetactive command to its correct state --- users/management/commands/clean_notyetactive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/users/management/commands/clean_notyetactive.py b/users/management/commands/clean_notyetactive.py index d1857096..14f7c826 100644 --- a/users/management/commands/clean_notyetactive.py +++ b/users/management/commands/clean_notyetactive.py @@ -28,13 +28,13 @@ from django.utils import timezone class Command(BaseCommand): - help = "Delete non members users (not yet active or disabled too long ago without an invoice)." + help = "Delete non members users (not yet active)." def handle(self, *args, **options): """First deleting invalid invoices, and then deleting the users""" days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") users_to_delete = ( - User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE) | Q(state=User.STATE_DISABLED)) + User.objects.filter(Q(state=User.STATE_NOT_YET_ACTIVE)) .filter(registered__lte=timezone.now() - timedelta(days=days)) .exclude(facture__valid=True) .distinct() From dc235b363ceccf21690b0984cccd7a0d8d9f1d74 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 19:11:59 +0200 Subject: [PATCH 080/490] Remove unused translation --- logs/locale/fr/LC_MESSAGES/django.po | 4 ---- 1 file changed, 4 deletions(-) diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 92dbb89b..66bc7234 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -243,10 +243,6 @@ msgstr "Utilisateurs complètement archivés" msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:264 -msgid "Waiting for email confirmation users" -msgstr "Utilisateurs en attente de confirmation d'email" - #: logs/views.py:273 msgid "Contributing members" msgstr "Adhérents cotisants" From b43bbf9d98570191357ca45d68eb501e9322b2c9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 19:59:25 +0200 Subject: [PATCH 081/490] Confirm email after resetting password --- users/views.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/users/views.py b/users/views.py index 027896ed..a2a62316 100644 --- a/users/views.py +++ b/users/views.py @@ -1025,8 +1025,10 @@ def process_passwd(request, req): u_form = PassForm(request.POST or None, instance=user, user=request.user) if u_form.is_valid(): with transaction.atomic(), reversion.create_revision(): + user.confirm_mail() u_form.save() reversion.set_comment("Password reset") + req.delete() messages.success(request, _("The password was changed.")) return redirect(reverse("index")) From 9fe43135403514e525269365a2974008fcb6d314 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:23:28 +0200 Subject: [PATCH 082/490] Move some code away from the forms --- users/forms.py | 19 ------------------- users/models.py | 21 +++++++++++++++++++++ users/views.py | 24 ++++++++++-------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/users/forms.py b/users/forms.py index 441d645c..effb9623 100644 --- a/users/forms.py +++ b/users/forms.py @@ -333,7 +333,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["room"].label = _("Room") self.fields["room"].empty_label = _("No room") self.fields["school"].empty_label = _("Select a school") - self.is_anon = kwargs["user"].is_anonymous() class Meta: model = Adherent @@ -382,23 +381,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): remove_user_room(room) return - def save(self, commit=True): - """On met à jour l'état de l'utilisateur en fonction de son mail""" - user = super(AdherentForm, self).save(commit=commit) - - if not self.is_anon and self.initial["email"] and user.email != self.initial["email"]: - # Send a confirmation email - if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE]: - user.email_state = User.EMAIL_STATE_PENDING - self.should_send_confirmation_email = True - - # Always keep the oldest change date - if user.email_change_date is None: - user.email_change_date = timezone.now() - - user.save() - return user - class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. @@ -518,7 +500,6 @@ class AdherentCreationForm(AdherentForm): an email to init the password should be sent""" # Save the provided password in hashed format user = super(AdherentForm, self).save(commit=False) - user.email_change_date = timezone.now() is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") send_email = not is_set_password_allowed or self.cleaned_data.get("init_password_by_mail") diff --git a/users/models.py b/users/models.py index 9c43561f..b9162f5b 100755 --- a/users/models.py +++ b/users/models.py @@ -800,6 +800,26 @@ class User( ) return + def send_confirm_email_if_necessary(self, request): + """Update the user's email state + Returns whether an email was sent""" + # Only update the state if the email changed + if self.__original_email == self.email: + return False + + # Archived users shouldn't get an email + if self.state not in [self.STATE_ACTIVE, self.STATE_DISABLED, self.STATE_NOT_YET_ACTIVE]: + return False + + # Always keep the oldest change date + if self.email_change_date is None: + self.email_change_date = timezone.now() + + self.email_state = self.EMAIL_STATE_PENDING + self.confirm_email_address_mail(request) + + return True + def confirm_email_before_date(self): if self.email_change_date is None or self.email_state == self.EMAIL_STATE_VERIFIED: return None @@ -810,6 +830,7 @@ class User( def confirm_email_address_mail(self, request): """Prend en argument un request, envoie un mail pour confirmer l'adresse""" + # Create the request and send the email req = Request() req.type = Request.EMAIL req.user = self diff --git a/users/views.py b/users/views.py index a2a62316..2366e417 100644 --- a/users/views.py +++ b/users/views.py @@ -124,11 +124,9 @@ def new_user(request): is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") if user.is_valid(): - user = user.save() - # Use "is False" so that if None, the email is sent - if is_set_password_allowed and user.should_send_password_reset_email is False: - user.confirm_email_address_mail(request) + if is_set_password_allowed and user.should_send_password_reset_email: + user.send_confirm_email_if_necessary(request) messages.success( request, _("The user %s was created, a confirmation email was sent.") @@ -142,6 +140,7 @@ def new_user(request): % user.pseudo, ) + user = user.save() return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) # Anonymous users are allowed to create new accounts @@ -223,13 +222,12 @@ def edit_info(request, user, userid): ) if user_form.is_valid(): if user_form.changed_data: + if user.send_confirm_email_if_necessary(request): + messages.success(request, _("Sent a new confirmation email.")) + user = user_form.save() messages.success(request, _("The user was edited.")) - if user_form.should_send_confirmation_email: - user.confirm_email_address_mail(request) - messages.success(request, _("Sent a new confirmation email.")) - return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": user_form, "action_name": _("Edit")}, @@ -544,14 +542,12 @@ def edit_email_settings(request, user_instance, **_kwargs): ) if email_settings.is_valid(): if email_settings.changed_data: + if user_instance.send_confirm_email_if_necessary(request): + messages.success(request, _("An email to confirm your address was sent.")) + email_settings.save() messages.success(request, _("The email settings were edited.")) - # Send confirmation email if necessary - if email_settings.should_send_confirmation_email is True: - user_instance.confirm_email_address_mail(request) - messages.success(request, _("An email to confirm your address was sent.")) - return redirect( reverse("users:profil", kwargs={"userid": str(user_instance.id)}) ) @@ -1073,7 +1069,7 @@ def resend_confirmation_email(request, logged_user, userid): messages.error(request, _("The user doesn't exist.")) if request.method == "POST": - user.confirm_email_address_mail(request) + user.send_confirm_email_if_necessary(request) messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) From 8b75900103171ce346e0b407d400620e3bcf9c3c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:41:02 +0200 Subject: [PATCH 083/490] Make AdherentCreationForm code clearer --- users/forms.py | 6 +++--- users/views.py | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/users/forms.py b/users/forms.py index effb9623..dea3c317 100644 --- a/users/forms.py +++ b/users/forms.py @@ -502,11 +502,11 @@ class AdherentCreationForm(AdherentForm): user = super(AdherentForm, self).save(commit=False) is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") - send_email = not is_set_password_allowed or self.cleaned_data.get("init_password_by_mail") - if not send_email: + set_passwd = is_set_password_allowed and not self.cleaned_data.get("init_password_by_mail") + if set_passwd: user.set_password(self.cleaned_data["password1"]) - user.should_send_password_reset_email = send_email + user.did_set_initial_passwd = set_passwd user.save() return user diff --git a/users/views.py b/users/views.py index 2366e417..06f65dce 100644 --- a/users/views.py +++ b/users/views.py @@ -124,8 +124,7 @@ def new_user(request): is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") if user.is_valid(): - # Use "is False" so that if None, the email is sent - if is_set_password_allowed and user.should_send_password_reset_email: + if user.did_set_initial_passwd: user.send_confirm_email_if_necessary(request) messages.success( request, From db620e9c90149b0aca3814c7703419003ad00aa0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:50:26 +0200 Subject: [PATCH 084/490] Always send confirmation email, except for fully-archived users --- users/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/users/models.py b/users/models.py index b9162f5b..d1d8c86a 100755 --- a/users/models.py +++ b/users/models.py @@ -807,15 +807,16 @@ class User( if self.__original_email == self.email: return False - # Archived users shouldn't get an email - if self.state not in [self.STATE_ACTIVE, self.STATE_DISABLED, self.STATE_NOT_YET_ACTIVE]: + self.email_state = self.EMAIL_STATE_PENDING + + # Fully archived users shouldn't get an email + if self.state != self.STATE_FULL_ARCHIVE: return False # Always keep the oldest change date if self.email_change_date is None: self.email_change_date = timezone.now() - self.email_state = self.EMAIL_STATE_PENDING self.confirm_email_address_mail(request) return True From 1a997f81eeba1bdff45d643544244a9950892f4f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:50:47 +0200 Subject: [PATCH 085/490] Add missing __original_email init --- users/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/users/models.py b/users/models.py index d1d8c86a..c9730167 100755 --- a/users/models.py +++ b/users/models.py @@ -1291,6 +1291,7 @@ class User( "room": self.can_change_room, } self.__original_state = self.state + self.__original_email = self.email def clean(self, *args, **kwargs): """Check if this pseudo is already used by any mailalias. From 8e8d03543945dd8666589de7f3543d158fbb1947 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:53:10 +0200 Subject: [PATCH 086/490] Save before checking if confirmation email should be sent --- users/views.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/users/views.py b/users/views.py index 06f65dce..4b539b9d 100644 --- a/users/views.py +++ b/users/views.py @@ -124,6 +124,8 @@ def new_user(request): is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") if user.is_valid(): + user = user.save() + if user.did_set_initial_passwd: user.send_confirm_email_if_necessary(request) messages.success( @@ -139,7 +141,6 @@ def new_user(request): % user.pseudo, ) - user = user.save() return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) # Anonymous users are allowed to create new accounts @@ -221,12 +222,12 @@ def edit_info(request, user, userid): ) if user_form.is_valid(): if user_form.changed_data: - if user.send_confirm_email_if_necessary(request): - messages.success(request, _("Sent a new confirmation email.")) - user = user_form.save() messages.success(request, _("The user was edited.")) + if user.send_confirm_email_if_necessary(request): + messages.success(request, _("Sent a new confirmation email.")) + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": user_form, "action_name": _("Edit")}, @@ -541,12 +542,12 @@ def edit_email_settings(request, user_instance, **_kwargs): ) if email_settings.is_valid(): if email_settings.changed_data: - if user_instance.send_confirm_email_if_necessary(request): - messages.success(request, _("An email to confirm your address was sent.")) - email_settings.save() messages.success(request, _("The email settings were edited.")) + if user_instance.send_confirm_email_if_necessary(request): + messages.success(request, _("An email to confirm your address was sent.")) + return redirect( reverse("users:profil", kwargs={"userid": str(user_instance.id)}) ) From 9f1c6a57e27630de12cc0ea7010ae013ec46c768 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 19:11:24 +0000 Subject: [PATCH 087/490] Correctly send a confirmation email --- users/models.py | 4 ++-- users/views.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/users/models.py b/users/models.py index c9730167..2c71c466 100755 --- a/users/models.py +++ b/users/models.py @@ -810,15 +810,15 @@ class User( self.email_state = self.EMAIL_STATE_PENDING # Fully archived users shouldn't get an email - if self.state != self.STATE_FULL_ARCHIVE: + if self.state == self.STATE_FULL_ARCHIVE: return False # Always keep the oldest change date if self.email_change_date is None: self.email_change_date = timezone.now() + self.save() self.confirm_email_address_mail(request) - return True def confirm_email_before_date(self): diff --git a/users/views.py b/users/views.py index 4b539b9d..551fd350 100644 --- a/users/views.py +++ b/users/views.py @@ -1069,7 +1069,7 @@ def resend_confirmation_email(request, logged_user, userid): messages.error(request, _("The user doesn't exist.")) if request.method == "POST": - user.send_confirm_email_if_necessary(request) + user.confirm_email_address_mail(request) messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) From c00bb2573b064c58037b857f43c7fb4e63fadfbf Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 17 Apr 2020 21:15:18 +0200 Subject: [PATCH 088/490] Remove code mort --- users/forms.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/users/forms.py b/users/forms.py index dea3c317..25e6c1b4 100644 --- a/users/forms.py +++ b/users/forms.py @@ -351,8 +351,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): label=_("Force the move?"), initial=False, required=False ) - should_send_confirmation_email = False - def clean_email(self): if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( "email" @@ -826,7 +824,6 @@ class EMailAddressForm(FormRevMixin, ModelForm): class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Edit email-related settings""" - should_send_confirmation_email = False def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -849,22 +846,6 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): ) ) - def save(self, commit=True): - """Update email state if email was changed""" - user = super(EmailSettingsForm, self).save(commit=commit) - - if self.initial["email"] and user.email != self.initial["email"]: - # Send a confirmation email - if user.state in [User.STATE_ACTIVE, User.STATE_DISABLED, User.STATE_NOT_YET_ACTIVE]: - user.email_state = User.EMAIL_STATE_PENDING - self.should_send_confirmation_email = True - - # Always keep the oldest change date - if user.email_change_date is None: - user.email_change_date = timezone.now() - - user.save() - class Meta: model = User fields = ["email", "local_email_enabled", "local_email_redirect"] From aad7110fd2e06da8f35d7618166a4e9498b505fd Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 21:18:10 +0200 Subject: [PATCH 089/490] Improve check did_set_initial_passwd in new_user --- users/forms.py | 1 - users/views.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/users/forms.py b/users/forms.py index 25e6c1b4..687c9e11 100644 --- a/users/forms.py +++ b/users/forms.py @@ -504,7 +504,6 @@ class AdherentCreationForm(AdherentForm): if set_passwd: user.set_password(self.cleaned_data["password1"]) - user.did_set_initial_passwd = set_passwd user.save() return user diff --git a/users/views.py b/users/views.py index 551fd350..78c0332e 100644 --- a/users/views.py +++ b/users/views.py @@ -126,7 +126,7 @@ def new_user(request): if user.is_valid(): user = user.save() - if user.did_set_initial_passwd: + if user.pwd_ntlm: user.send_confirm_email_if_necessary(request) messages.success( request, From b88fea3b4b801d65392b92d392aa9a39a4cc87b0 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Fri, 17 Apr 2020 22:08:40 +0200 Subject: [PATCH 090/490] Test sur le mot primaire au lieu du mot de pass radius --- users/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/views.py b/users/views.py index 78c0332e..77e40701 100644 --- a/users/views.py +++ b/users/views.py @@ -126,7 +126,7 @@ def new_user(request): if user.is_valid(): user = user.save() - if user.pwd_ntlm: + if user.password: user.send_confirm_email_if_necessary(request) messages.success( request, From 216d14bb25c091753eb83b539f0de8bc9d6a6d84 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 20:12:12 +0000 Subject: [PATCH 091/490] Ensure confirmation email tokens are deleted if no longer valid --- users/migrations/0087_request_email.py | 20 ++++++++++++++++++++ users/models.py | 6 ++++++ 2 files changed, 26 insertions(+) create mode 100644 users/migrations/0087_request_email.py diff --git a/users/migrations/0087_request_email.py b/users/migrations/0087_request_email.py new file mode 100644 index 00000000..3cb8d792 --- /dev/null +++ b/users/migrations/0087_request_email.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 20:10 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0086_user_email_change_date'), + ] + + operations = [ + migrations.AddField( + model_name='request', + name='email', + field=models.EmailField(blank=True, max_length=254, null=True), + ), + ] diff --git a/users/models.py b/users/models.py index 2c71c466..8431b0b3 100755 --- a/users/models.py +++ b/users/models.py @@ -831,10 +831,15 @@ class User( def confirm_email_address_mail(self, request): """Prend en argument un request, envoie un mail pour confirmer l'adresse""" + # Delete all older requests for this user, that aren't for this email + filter = Q(user=self) & Q(type=Request.EMAIL) & ~Q(email=self.email) + Request.objects.filter(filter).delete() + # Create the request and send the email req = Request() req.type = Request.EMAIL req.user = self + req.email = self.email req.save() template = loader.get_template("users/email_confirmation_request") @@ -1873,6 +1878,7 @@ class Request(models.Model): type = models.CharField(max_length=2, choices=TYPE_CHOICES) token = models.CharField(max_length=32) user = models.ForeignKey("User", on_delete=models.CASCADE) + email = models.EmailField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True, editable=False) expires_at = models.DateTimeField() From a7b989b7deaba8334e62e7e109e0f7feba38cfff Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 22:20:45 +0200 Subject: [PATCH 092/490] Fix error in profil of users with an unverified email --- users/locale/fr/LC_MESSAGES/django.po | 4 ++++ users/templates/users/profil.html | 21 ++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 922c2dbe..e19e19c6 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -1028,6 +1028,10 @@ msgstr "Pas de connexion" msgid "Pay for a connection" msgstr "Payer une connexion" +#: users/templates/users/profil.html:81 +msgid "Resend the email" +msgstr "Renvoyer le mail" + #: users/templates/users/profil.html:60 msgid "Ask someone with the appropriate rights to pay for a connection." msgstr "" diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 82feb8e6..4fec3c18 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -70,14 +70,21 @@ with this program; if not, write to the Free Software Foundation, Inc., {% elif not users.has_access %}
{% trans "No connection" %}
+
- {% can_create Facture %} - - {% trans "Pay for a connection" %} - - {% acl_else %} - {% trans "Ask someone with the appropriate rights to pay for a connection." %} - {% acl_end %} + {% if users.email_state == users.EMAIL_STATE_UNVERIFIED %} + + {% trans "Resend the email" %} + + {% else %} + {% can_create Facture %} + + {% trans "Pay for a connection" %} + + {% acl_else %} + {% trans "Ask someone with the appropriate rights to pay for a connection." %} + {% acl_end %} + {% endif %}
{% else %} From def91bff7af7af58979cd7ad9e1a37fe2c99eab8 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 22:49:50 +0200 Subject: [PATCH 093/490] Call user.confirm_mail in UserManager._create_user --- users/models.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/users/models.py b/users/models.py index 8431b0b3..f6596fa9 100755 --- a/users/models.py +++ b/users/models.py @@ -147,8 +147,7 @@ class UserManager(BaseUserManager): ) user.set_password(password) - user.email_change_date = None - user.email_state = User.EMAIL_STATE_VERIFIED + user.confirm_mail() if su: user.is_superuser = True user.save(using=self._db) From 1c65dc50ff058a1603c306c81b75a7c9bc9b673d Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 22:54:27 +0200 Subject: [PATCH 094/490] Don't set User.email_change_date to None --- users/models.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/users/models.py b/users/models.py index f6596fa9..11db2a8e 100755 --- a/users/models.py +++ b/users/models.py @@ -812,8 +812,8 @@ class User( if self.state == self.STATE_FULL_ARCHIVE: return False - # Always keep the oldest change date - if self.email_change_date is None: + # Don't allow users without a confirmed email to postpone their due date + if self.state == self.STATE_ACTIVE or not self.email_change_date: self.email_change_date = timezone.now() self.save() @@ -821,7 +821,7 @@ class User( return True def confirm_email_before_date(self): - if self.email_change_date is None or self.email_state == self.EMAIL_STATE_VERIFIED: + if self.email_state == self.EMAIL_STATE_VERIFIED: return None days = OptionalUser.get_cached_value("disable_emailnotyetconfirmed") @@ -946,8 +946,6 @@ class User( def confirm_mail(self): """Marque l'email de l'utilisateur comme confirmé""" - # Reset the email change date and update the email status - self.email_change_date = None self.email_state = self.EMAIL_STATE_VERIFIED @cached_property From 5fdb8a7b1e0ce30ae6273ee6d546ab2a6e046d8e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 22:57:41 +0200 Subject: [PATCH 095/490] Fix wrong state check in send_confirm_email_if_necessary --- users/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/models.py b/users/models.py index 11db2a8e..4345050e 100755 --- a/users/models.py +++ b/users/models.py @@ -813,7 +813,7 @@ class User( return False # Don't allow users without a confirmed email to postpone their due date - if self.state == self.STATE_ACTIVE or not self.email_change_date: + if self.email_state == self.EMAIL_STATE_VERIFIED or not self.email_change_date: self.email_change_date = timezone.now() self.save() From 154096050c8ef4c679f3875177e0b1a4a6618e0c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 23:07:40 +0200 Subject: [PATCH 096/490] Make send_confirm_email_if_necessary clearer --- users/models.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/users/models.py b/users/models.py index 4345050e..1baac3a8 100755 --- a/users/models.py +++ b/users/models.py @@ -800,23 +800,31 @@ class User( return def send_confirm_email_if_necessary(self, request): - """Update the user's email state + """Update the user's email state: + * If the user changed email, it needs to be confirmed + * If they're not fully archived, send a confirmation email + Returns whether an email was sent""" # Only update the state if the email changed if self.__original_email == self.email: return False - self.email_state = self.EMAIL_STATE_PENDING + # If the user was previously in the PENDING or UNVERIFIED state, + # we can't update email_change_date otherwise it would push back + # their due date + # However, if the user is in the VERIFIED state, we reset the date + if self.email_state == self.EMAIL_STATE_VERIFIED: + self.email_change_date = timezone.now() - # Fully archived users shouldn't get an email + # Remember that the user needs to confirm their email address again + self.email_state = self.EMAIL_STATE_PENDING + self.save() + + # Fully archived users shouldn't get an email, so stop here if self.state == self.STATE_FULL_ARCHIVE: return False - # Don't allow users without a confirmed email to postpone their due date - if self.email_state == self.EMAIL_STATE_VERIFIED or not self.email_change_date: - self.email_change_date = timezone.now() - - self.save() + # Send the email self.confirm_email_address_mail(request) return True From cdd9dfae6c8757302d366c1c135ad72e1cbd0016 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 21:13:16 +0000 Subject: [PATCH 097/490] Provide default value for email_change_date and don't allow it to be null --- users/migrations/0088_auto_20200417_2312.py | 23 +++++++++++++++++++++ users/models.py | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 users/migrations/0088_auto_20200417_2312.py diff --git a/users/migrations/0088_auto_20200417_2312.py b/users/migrations/0088_auto_20200417_2312.py new file mode 100644 index 00000000..e9f82f7c --- /dev/null +++ b/users/migrations/0088_auto_20200417_2312.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 21:12 +from __future__ import unicode_literals + +import datetime +from django.db import migrations, models +from django.utils.timezone import utc + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0087_request_email'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email_change_date', + field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2020, 4, 17, 21, 12, 19, 739799, tzinfo=utc)), + preserve_default=False, + ), + ] diff --git a/users/models.py b/users/models.py index 1baac3a8..613aa9ef 100755 --- a/users/models.py +++ b/users/models.py @@ -238,7 +238,7 @@ class User( shortcuts_enabled = models.BooleanField( verbose_name=_("enable shortcuts on Re2o website"), default=True ) - email_change_date = models.DateTimeField(default=None, null=True) + email_change_date = models.DateTimeField(auto_now_add=True) USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] From fb5c215f4736a466b80ef0c07f35e8954bcc82bb Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 00:12:22 +0200 Subject: [PATCH 098/490] Allow admin to modify email state --- users/forms.py | 4 ++-- users/locale/fr/LC_MESSAGES/django.po | 4 ++-- users/models.py | 10 ++++++++++ users/views.py | 6 ++++-- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/users/forms.py b/users/forms.py index 687c9e11..158965d6 100644 --- a/users/forms.py +++ b/users/forms.py @@ -650,11 +650,11 @@ class EditServiceUserForm(ServiceUserForm): class StateForm(FormRevMixin, ModelForm): - """ Changement de l'état d'un user""" + """Change state of an user, and if its main email is verified or not""" class Meta: model = User - fields = ["state"] + fields = ["state" ,"email_state"] def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index e19e19c6..77e5a4f9 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -1360,8 +1360,8 @@ msgid "Sent a new confirmation email." msgstr "Un nouveau mail de confirmation a été envoyé." #: users/views.py:225 -msgid "The state was edited." -msgstr "L'état a été modifié." +msgid "The states were edited." +msgstr "Les états ont été modifié." #: users/views.py:242 msgid "The groups were edited." diff --git a/users/models.py b/users/models.py index 613aa9ef..56f411b1 100755 --- a/users/models.py +++ b/users/models.py @@ -828,6 +828,16 @@ class User( self.confirm_email_address_mail(request) return True + def trigger_email_changed_state(self, request): + """Trigger an email, and changed values after email_state been manually updated""" + if self.email_state == self.EMAIL_STATE_VERIFIED: + return False + + self.email_change_date = timezone.now() + + self.confirm_email_address_mail(request) + return True + def confirm_email_before_date(self): if self.email_state == self.EMAIL_STATE_VERIFIED: return None diff --git a/users/views.py b/users/views.py index 77e40701..3d5b0205 100644 --- a/users/views.py +++ b/users/views.py @@ -243,8 +243,10 @@ def state(request, user, userid): state_form = StateForm(request.POST or None, instance=user) if state_form.is_valid(): if state_form.changed_data: - state_form.save() - messages.success(request, _("The state was edited.")) + user_instance = state_form.save() + messages.success(request, _("The states were edited.")) + if user_instance.trigger_email_changed_state(request): + messages.success(request, _("An email to confirm the address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( {"userform": state_form, "action_name": _("Edit")}, From d2881c64dc7939e0b0e2dde9f96b1aa9ce4ade2f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 00:16:27 +0200 Subject: [PATCH 099/490] Don't forget to save --- users/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/users/models.py b/users/models.py index 56f411b1..689b0bf0 100755 --- a/users/models.py +++ b/users/models.py @@ -834,6 +834,7 @@ class User( return False self.email_change_date = timezone.now() + self.save() self.confirm_email_address_mail(request) return True From 6c7833ebb1e60ff555755e1389cfe7c6331e3e2b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 00:29:50 +0200 Subject: [PATCH 100/490] Add missing translations --- search/locale/fr/LC_MESSAGES/django.po | 16 ++++++++++++---- users/forms.py | 4 +++- users/locale/fr/LC_MESSAGES/django.po | 12 ++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 6da1a477..c5d18323 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -50,10 +50,6 @@ msgstr "Pas encore adhéré" msgid "Fully archived" msgstr "Complètement archivés" -#: search/forms.py:38 -msgid "Waiting for email confirmation" -msgstr "En attente de confirmation d'email" - #: search/forms.py:41 msgid "Users" msgstr "Utilisateurs" @@ -117,6 +113,18 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" +#: search/models.py:195 +msgid "Verified" +msgstr "Confirmé" + +#: search/models.py:196 +msgid "Unverified" +msgstr "Non-confirmé" + +#: search/models.py:197 +msgid "Waiting for email confirmation" +msgstr "En attente de confirmation d'email" + #: search/templates/search/index.html:29 msgid "Search results" msgstr "Résultats de la recherche" diff --git a/users/forms.py b/users/forms.py index 158965d6..8e60b698 100644 --- a/users/forms.py +++ b/users/forms.py @@ -654,11 +654,13 @@ class StateForm(FormRevMixin, ModelForm): class Meta: model = User - fields = ["state" ,"email_state"] + fields = ["state", "email_state"] def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(StateForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields["state"].label = _("State") + self.fields["email_state"].label = _("Email state") class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 77e5a4f9..c53cc94f 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -223,6 +223,14 @@ msgstr "Nom" msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" +#: users/forms.py:662 +msgid "State" +msgstr "État" + +#: users/forms.py:663 +msgid "Email state" +msgstr "État du mail" + #: users/forms.py:601 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" @@ -1367,6 +1375,10 @@ msgstr "Les états ont été modifié." msgid "The groups were edited." msgstr "Les groupes ont été modifiés." +#: users/views.py:249 +msgid "An email to confirm the address was sent." +msgstr "Un mail pour confirmer l'adresse a été envoyé." + #: users/views.py:261 users/views.py:998 msgid "The password was changed." msgstr "Le mot de passe a été changé." From 5b4dfea87869f0b49d828dd3724b98c04568183f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 00:33:18 +0200 Subject: [PATCH 101/490] Remove duplicate translation --- users/locale/fr/LC_MESSAGES/django.po | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index c53cc94f..818c973a 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -223,10 +223,6 @@ msgstr "Nom" msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:662 -msgid "State" -msgstr "État" - #: users/forms.py:663 msgid "Email state" msgstr "État du mail" @@ -1146,7 +1142,7 @@ msgstr "Bannissement" msgid "Not banned" msgstr "Non banni" -#: users/templates/users/profil.html:251 +#: users/templates/users/profil.html:251 users/forms.py:662 msgid "State" msgstr "État" From 179b8cfd26e2c31aebe3ad89eeba740077daf93b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 00:39:06 +0200 Subject: [PATCH 102/490] Make email translations more consistent --- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 4 ++-- users/models.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index e7ac2e66..8341c1b9 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -315,7 +315,7 @@ msgstr "" "Si True, les utilisateurs ont le choix de recevoir un email avec" " un lien pour changer leur mot de passe lors de la création de leur compte, " " ou alors de choisir leur mot de passe immédiatement." -" Si False, un email est toujours envoyé." +" Si False, un mail est toujours envoyé." #: preferences/models.py:121 msgid "If True, archived users are allowed to connect." diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index c5d18323..e03da256 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -123,7 +123,7 @@ msgstr "Non-confirmé" #: search/models.py:197 msgid "Waiting for email confirmation" -msgstr "En attente de confirmation d'email" +msgstr "En attente de confirmation du mail" #: search/templates/search/index.html:29 msgid "Search results" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 818c973a..e2a8f3ce 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -979,7 +979,7 @@ msgstr "Email de confirmation" #: users/templates/users/confirm_email.html:36 #, python-format msgid "Confirm the email" -msgstr "Confirmer l'email" +msgstr "Confirmer le mail" #: users/templates/users/confirm_email.html:36 #, python-format @@ -1263,7 +1263,7 @@ msgstr "Renvoyer l'email de confirmation ?" #: users/templates/users/resend_confirmation_email.html:36 #, python-format msgid "The confirmation email will be sent to" -msgstr "L'email de confirmation sera envoyé à" +msgstr "Le mail de confirmation sera envoyé à" #: users/templates/users/sidebar.html:33 msgid "Create a club or organisation" diff --git a/users/models.py b/users/models.py index 689b0bf0..e956a26a 100755 --- a/users/models.py +++ b/users/models.py @@ -874,7 +874,7 @@ class User( "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), } send_mail( - "Confirmation de l'email de %(name)s / Email confirmation for " + "Confirmation du mail de %(name)s / Email confirmation for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, template.render(context), GeneralOption.get_cached_value("email_from"), From 2ed3b2ac3186a504c29aae6926765b8957da16dd Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 22:52:30 +0000 Subject: [PATCH 103/490] Fix translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 228 +++---- logs/locale/fr/LC_MESSAGES/django.po | 4 +- machines/locale/fr/LC_MESSAGES/django.po | 46 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 549 +++++++-------- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 68 +- templates/locale/fr/LC_MESSAGES/django.po | 10 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 719 ++++++++++---------- users/models.py | 4 +- 13 files changed, 826 insertions(+), 812 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index aba3d217..ccc73c63 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 1d68e1fe..344c4269 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" @@ -37,7 +37,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:676 +#: cotisations/forms.py:73 cotisations/models.py:682 msgid "Member" msgstr "Adhérent" @@ -154,8 +154,8 @@ msgstr "Peut voir un objet facture" msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:145 cotisations/models.py:431 cotisations/views.py:378 -#: cotisations/views.py:573 +#: cotisations/models.py:145 cotisations/models.py:456 cotisations/views.py:376 +#: cotisations/views.py:571 msgid "invoice" msgstr "facture" @@ -217,115 +217,115 @@ msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:347 +#: cotisations/models.py:372 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:349 +#: cotisations/models.py:374 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:350 +#: cotisations/models.py:375 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:351 +#: cotisations/models.py:376 msgid "address" msgstr "adresse" -#: cotisations/models.py:352 +#: cotisations/models.py:377 msgid "paid" msgstr "payé" -#: cotisations/models.py:353 +#: cotisations/models.py:378 msgid "remark" msgstr "remarque" -#: cotisations/models.py:358 +#: cotisations/models.py:383 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:361 +#: cotisations/models.py:386 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:396 +#: cotisations/models.py:421 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:402 +#: cotisations/models.py:427 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:424 cotisations/models.py:682 -#: cotisations/models.py:940 +#: cotisations/models.py:449 cotisations/models.py:688 +#: cotisations/models.py:946 msgid "Connection" msgstr "Connexion" -#: cotisations/models.py:425 cotisations/models.py:683 -#: cotisations/models.py:941 +#: cotisations/models.py:450 cotisations/models.py:689 +#: cotisations/models.py:947 msgid "Membership" msgstr "Adhésion" -#: cotisations/models.py:426 cotisations/models.py:678 -#: cotisations/models.py:684 cotisations/models.py:942 +#: cotisations/models.py:451 cotisations/models.py:684 +#: cotisations/models.py:690 cotisations/models.py:948 msgid "Both of them" msgstr "Les deux" -#: cotisations/models.py:435 +#: cotisations/models.py:460 msgid "amount" msgstr "montant" -#: cotisations/models.py:438 +#: cotisations/models.py:463 msgid "article" msgstr "article" -#: cotisations/models.py:441 +#: cotisations/models.py:466 msgid "price" msgstr "prix" -#: cotisations/models.py:444 cotisations/models.py:696 +#: cotisations/models.py:469 cotisations/models.py:702 msgid "duration (in months)" msgstr "durée (en mois)" -#: cotisations/models.py:450 cotisations/models.py:702 +#: cotisations/models.py:475 cotisations/models.py:708 msgid "duration (in days, will be added to duration in months)" msgstr "durée (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:458 cotisations/models.py:716 -#: cotisations/models.py:953 +#: cotisations/models.py:483 cotisations/models.py:722 +#: cotisations/models.py:959 msgid "subscription type" msgstr "type de cotisation" -#: cotisations/models.py:463 +#: cotisations/models.py:488 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:464 +#: cotisations/models.py:489 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:466 cotisations/models.py:947 +#: cotisations/models.py:491 cotisations/models.py:953 msgid "purchase" msgstr "achat" -#: cotisations/models.py:467 +#: cotisations/models.py:492 msgid "purchases" msgstr "achats" -#: cotisations/models.py:539 cotisations/models.py:736 +#: cotisations/models.py:545 cotisations/models.py:742 msgid "Duration must be specified for a subscription." msgstr "La durée doit être renseignée pour une cotisation." -#: cotisations/models.py:550 +#: cotisations/models.py:556 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:556 +#: cotisations/models.py:562 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:565 +#: cotisations/models.py:571 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -333,15 +333,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:580 +#: cotisations/models.py:586 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:586 +#: cotisations/models.py:592 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:593 +#: cotisations/models.py:599 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -349,134 +349,134 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:609 +#: cotisations/models.py:615 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:677 +#: cotisations/models.py:683 msgid "Club" msgstr "Club" -#: cotisations/models.py:687 +#: cotisations/models.py:693 msgid "designation" msgstr "désignation" -#: cotisations/models.py:690 +#: cotisations/models.py:696 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:708 +#: cotisations/models.py:714 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:719 cotisations/models.py:820 +#: cotisations/models.py:725 cotisations/models.py:826 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:726 +#: cotisations/models.py:732 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:727 +#: cotisations/models.py:733 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:734 +#: cotisations/models.py:740 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:759 +#: cotisations/models.py:765 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:800 +#: cotisations/models.py:806 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:801 +#: cotisations/models.py:807 msgid "bank" msgstr "banque" -#: cotisations/models.py:802 +#: cotisations/models.py:808 msgid "banks" msgstr "banques" -#: cotisations/models.py:818 +#: cotisations/models.py:824 msgid "method" msgstr "moyen" -#: cotisations/models.py:825 +#: cotisations/models.py:831 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:826 +#: cotisations/models.py:832 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:832 +#: cotisations/models.py:838 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:833 +#: cotisations/models.py:839 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:835 +#: cotisations/models.py:841 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:836 +#: cotisations/models.py:842 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:875 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:881 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:885 +#: cotisations/models.py:891 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:905 +#: cotisations/models.py:911 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:924 +#: cotisations/models.py:930 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:955 +#: cotisations/models.py:961 msgid "start date" msgstr "date de début" -#: cotisations/models.py:956 +#: cotisations/models.py:962 msgid "end date" msgstr "date de fin" -#: cotisations/models.py:960 +#: cotisations/models.py:966 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:961 +#: cotisations/models.py:967 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:963 +#: cotisations/models.py:969 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:964 +#: cotisations/models.py:970 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:970 +#: cotisations/models.py:976 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:979 +#: cotisations/models.py:985 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -484,11 +484,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:991 +#: cotisations/models.py:997 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:998 +#: cotisations/models.py:1004 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -496,7 +496,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1014 +#: cotisations/models.py:1020 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -784,7 +784,7 @@ msgstr "Contrôlé" #: cotisations/templates/cotisations/control.html:107 #: cotisations/templates/cotisations/delete.html:38 #: cotisations/templates/cotisations/edit_facture.html:64 -#: cotisations/views.py:168 cotisations/views.py:222 cotisations/views.py:278 +#: cotisations/views.py:170 cotisations/views.py:222 cotisations/views.py:276 msgid "Confirm" msgstr "Confirmer" @@ -921,7 +921,7 @@ msgstr "Rechargement de solde" msgid "Pay %(amount)s €" msgstr "Payer %(amount)s €" -#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1028 +#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1026 msgid "Pay" msgstr "Payer" @@ -933,11 +933,11 @@ msgstr "Créer une facture" msgid "Control the invoices" msgstr "Contrôler les factures" -#: cotisations/views.py:155 +#: cotisations/views.py:157 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." -#: cotisations/views.py:169 +#: cotisations/views.py:171 msgid "New invoice" msgstr "Nouvelle facture" @@ -949,104 +949,104 @@ msgstr "Le devis a été créé." msgid "New cost estimate" msgstr "Nouveau devis" -#: cotisations/views.py:272 +#: cotisations/views.py:270 msgid "The custom invoice was created." msgstr "La facture personnalisée a été créée." -#: cotisations/views.py:282 +#: cotisations/views.py:280 msgid "New custom invoice" msgstr "Nouvelle facture personnalisée" -#: cotisations/views.py:357 cotisations/views.py:438 +#: cotisations/views.py:355 cotisations/views.py:436 msgid "The invoice was edited." msgstr "La facture a été modifiée." -#: cotisations/views.py:375 cotisations/views.py:570 +#: cotisations/views.py:373 cotisations/views.py:568 msgid "The invoice was deleted." msgstr "La facture a été supprimée." -#: cotisations/views.py:398 +#: cotisations/views.py:396 msgid "The cost estimate was edited." msgstr "Le devis a été modifié." -#: cotisations/views.py:405 +#: cotisations/views.py:403 msgid "Edit cost estimate" msgstr "Modifier le devis" -#: cotisations/views.py:419 +#: cotisations/views.py:417 msgid "An invoice was successfully created from your cost estimate." msgstr "Une facture a bien été créée à partir de votre devis." -#: cotisations/views.py:445 +#: cotisations/views.py:443 msgid "Edit custom invoice" msgstr "Modifier la facture personnalisée" -#: cotisations/views.py:507 +#: cotisations/views.py:505 msgid "The cost estimate was deleted." msgstr "Le devis a été supprimé." -#: cotisations/views.py:510 +#: cotisations/views.py:508 msgid "cost estimate" msgstr "devis" -#: cotisations/views.py:594 +#: cotisations/views.py:592 msgid "The article was created." msgstr "L'article a été créé." -#: cotisations/views.py:599 cotisations/views.py:673 cotisations/views.py:767 +#: cotisations/views.py:597 cotisations/views.py:671 cotisations/views.py:765 msgid "Add" msgstr "Ajouter" -#: cotisations/views.py:600 +#: cotisations/views.py:598 msgid "New article" msgstr "Nouvel article" -#: cotisations/views.py:617 +#: cotisations/views.py:615 msgid "The article was edited." msgstr "L'article a été modifié." -#: cotisations/views.py:622 cotisations/views.py:705 cotisations/views.py:791 +#: cotisations/views.py:620 cotisations/views.py:703 cotisations/views.py:789 msgid "Edit" msgstr "Modifier" -#: cotisations/views.py:623 +#: cotisations/views.py:621 msgid "Edit article" msgstr "Modifier l'article" -#: cotisations/views.py:640 +#: cotisations/views.py:638 msgid "The articles were deleted." msgstr "Les articles ont été supprimés." -#: cotisations/views.py:645 cotisations/views.py:744 cotisations/views.py:829 +#: cotisations/views.py:643 cotisations/views.py:742 cotisations/views.py:827 msgid "Delete" msgstr "Supprimer" -#: cotisations/views.py:646 +#: cotisations/views.py:644 msgid "Delete article" msgstr "Supprimer l'article" -#: cotisations/views.py:667 +#: cotisations/views.py:665 msgid "The payment method was created." msgstr "Le moyen de paiment a été créé." -#: cotisations/views.py:674 +#: cotisations/views.py:672 msgid "New payment method" msgstr "Nouveau moyen de paiement" -#: cotisations/views.py:699 +#: cotisations/views.py:697 msgid "The payment method was edited." msgstr "Le moyen de paiment a été modifié." -#: cotisations/views.py:706 +#: cotisations/views.py:704 msgid "Edit payment method" msgstr "Modifier le moyen de paiement" -#: cotisations/views.py:728 +#: cotisations/views.py:726 #, python-format msgid "The payment method %(method_name)s was deleted." msgstr "Le moyen de paiement %(method_name)s a été supprimé." -#: cotisations/views.py:735 +#: cotisations/views.py:733 #, python-format msgid "" "The payment method %(method_name)s can't be deleted because there are " @@ -1055,32 +1055,32 @@ msgstr "" "Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a " "des factures qui l'utilisent." -#: cotisations/views.py:745 +#: cotisations/views.py:743 msgid "Delete payment method" msgstr "Supprimer le moyen de paiement" -#: cotisations/views.py:762 +#: cotisations/views.py:760 msgid "The bank was created." msgstr "La banque a été créée." -#: cotisations/views.py:768 +#: cotisations/views.py:766 msgid "New bank" msgstr "Nouvelle banque" -#: cotisations/views.py:786 +#: cotisations/views.py:784 msgid "The bank was edited." msgstr "La banque a été modifiée." -#: cotisations/views.py:792 +#: cotisations/views.py:790 msgid "Edit bank" msgstr "Modifier la banque" -#: cotisations/views.py:814 +#: cotisations/views.py:812 #, python-format msgid "The bank %(bank_name)s was deleted." msgstr "La banque %(bank_name)s a été supprimée." -#: cotisations/views.py:820 +#: cotisations/views.py:818 #, python-format msgid "" "The bank %(bank_name)s can't be deleted because there are invoices using it." @@ -1088,22 +1088,22 @@ msgstr "" "La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " "qui l'utilisent." -#: cotisations/views.py:830 +#: cotisations/views.py:828 msgid "Delete bank" msgstr "Supprimer la banque" -#: cotisations/views.py:864 +#: cotisations/views.py:862 msgid "Your changes have been properly taken into account." msgstr "Vos modifications ont correctement été prises en compte." -#: cotisations/views.py:996 +#: cotisations/views.py:994 msgid "You are not allowed to credit your balance." msgstr "Vous n'êtes pas autorisé à créditer votre solde." -#: cotisations/views.py:1027 +#: cotisations/views.py:1025 msgid "Refill your balance" msgstr "Recharger votre solde" -#: cotisations/views.py:1046 +#: cotisations/views.py:1044 msgid "Could not find a voucher for that invoice." msgstr "Impossible de trouver un reçu pour cette facture." diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 66bc7234..6a2bd167 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -243,7 +243,7 @@ msgstr "Utilisateurs complètement archivés" msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:273 +#: logs/views.py:264 msgid "Contributing members" msgstr "Adhérents cotisants" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 049c01ad..ff54149b 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -55,91 +55,91 @@ msgstr "Sélectionnez un type de machine" msgid "Automatic IPv4 assignment" msgstr "Assignation automatique IPv4" -#: machines/forms.py:177 +#: machines/forms.py:172 msgid "Current aliases" msgstr "Alias actuels" -#: machines/forms.py:199 +#: machines/forms.py:194 msgid "Machine type to add" msgstr "Type de machine à ajouter" -#: machines/forms.py:200 +#: machines/forms.py:195 msgid "Related IP type" msgstr "Type d'IP relié" -#: machines/forms.py:208 +#: machines/forms.py:203 msgid "Current machine types" msgstr "Types de machines actuels" -#: machines/forms.py:232 +#: machines/forms.py:227 msgid "IP type to add" msgstr "Type d'IP à ajouter" -#: machines/forms.py:260 +#: machines/forms.py:255 msgid "Current IP types" msgstr "Types d'IP actuels" -#: machines/forms.py:283 +#: machines/forms.py:278 msgid "Extension to add" msgstr "Extension à ajouter" -#: machines/forms.py:284 machines/templates/machines/aff_extension.html:37 +#: machines/forms.py:279 machines/templates/machines/aff_extension.html:37 msgid "A record origin" msgstr "Enregistrement A origin" -#: machines/forms.py:285 machines/templates/machines/aff_extension.html:39 +#: machines/forms.py:280 machines/templates/machines/aff_extension.html:39 msgid "AAAA record origin" msgstr "Enregistrement AAAA origin" -#: machines/forms.py:286 +#: machines/forms.py:281 msgid "SOA record to use" msgstr "Enregistrement SOA à utiliser" -#: machines/forms.py:287 +#: machines/forms.py:282 msgid "Sign with DNSSEC" msgstr "Signer avec DNSSEC" -#: machines/forms.py:295 +#: machines/forms.py:290 msgid "Current extensions" msgstr "Extensions actuelles" -#: machines/forms.py:337 +#: machines/forms.py:332 msgid "Current SOA records" msgstr "Enregistrements SOA actuels" -#: machines/forms.py:370 +#: machines/forms.py:365 msgid "Current MX records" msgstr "Enregistrements MX actuels" -#: machines/forms.py:405 +#: machines/forms.py:400 msgid "Current NS records" msgstr "Enregistrements NS actuels" -#: machines/forms.py:435 +#: machines/forms.py:430 msgid "Current TXT records" msgstr "Enregistrements TXT actuels" -#: machines/forms.py:465 +#: machines/forms.py:460 msgid "Current DNAME records" msgstr "Enregistrements DNAME actuels" -#: machines/forms.py:495 +#: machines/forms.py:490 msgid "Current SRV records" msgstr "Enregistrements SRV actuels" -#: machines/forms.py:526 +#: machines/forms.py:521 msgid "Current NAS devices" msgstr "Dispositifs NAS actuels" -#: machines/forms.py:559 +#: machines/forms.py:554 msgid "Current roles" msgstr "Rôles actuels" -#: machines/forms.py:601 +#: machines/forms.py:596 msgid "Current services" msgstr "Services actuels" -#: machines/forms.py:643 +#: machines/forms.py:638 msgid "Current VLANs" msgstr "VLANs actuels" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 4faf9166..4457f37a 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 8341c1b9..d48f427e 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,7 +35,7 @@ msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." #: preferences/forms.py:65 -#: preferences/templates/preferences/display_preferences.html:144 +#: preferences/templates/preferences/display_preferences.html:150 msgid "Telephone number required" msgstr "Numéro de téléphone requis" @@ -52,201 +52,207 @@ msgid "All can create a member" msgstr "Tous peuvent créer un adhérent" #: preferences/forms.py:69 +#: preferences/templates/preferences/display_preferences.html:134 +msgid "Delay before disabling accounts without a verified email" +msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" + +#: preferences/forms.py:70 #: preferences/templates/preferences/display_preferences.html:120 msgid "Self registration" msgstr "Autoinscription" -#: preferences/forms.py:70 -#: preferences/templates/preferences/display_preferences.html:120 -msgid "Allow directly setting a password during account creation" -msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" - -#: preferences/forms.py:70 +#: preferences/forms.py:71 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: preferences/forms.py:84 +#: preferences/forms.py:72 +msgid "Allow directly setting a password during account creation" +msgstr "" +"Permettre le choix d'un mot de passe directement lors de la création du " +"compte" + +#: preferences/forms.py:86 msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" -#: preferences/forms.py:87 -#: preferences/templates/preferences/display_preferences.html:174 +#: preferences/forms.py:89 +#: preferences/templates/preferences/display_preferences.html:180 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" -#: preferences/forms.py:90 -#: preferences/templates/preferences/display_preferences.html:178 +#: preferences/forms.py:92 +#: preferences/templates/preferences/display_preferences.html:184 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" -#: preferences/forms.py:92 +#: preferences/forms.py:94 msgid "IPv6 mode" msgstr "Mode IPv6" -#: preferences/forms.py:93 +#: preferences/forms.py:95 msgid "Can create a machine" msgstr "Peut créer une machine" -#: preferences/forms.py:136 +#: preferences/forms.py:138 msgid "General message in French" msgstr "Message général en français" -#: preferences/forms.py:137 +#: preferences/forms.py:139 msgid "General message in English" msgstr "Message général en anglais" -#: preferences/forms.py:139 +#: preferences/forms.py:141 #: preferences/templates/preferences/display_preferences.html:58 msgid "Number of results displayed when searching" msgstr "Nombre de résultats affichés lors de la recherche" -#: preferences/forms.py:142 +#: preferences/forms.py:144 msgid "Number of items per page, standard size (e.g. users)" msgstr "Nombre d'éléments par page, taille standard (ex : utilisateurs)" -#: preferences/forms.py:145 +#: preferences/forms.py:147 msgid "Number of items per page, large size (e.g. machines)" msgstr "Nombre d'éléments par page, taille importante (ex : machines)" -#: preferences/forms.py:148 +#: preferences/forms.py:150 #: preferences/templates/preferences/display_preferences.html:66 msgid "Time before expiration of the reset password link (in hours)" msgstr "" "Temps avant expiration du lien de réinitialisation de mot de passe (en " "heures)" -#: preferences/forms.py:150 +#: preferences/forms.py:152 #: preferences/templates/preferences/display_preferences.html:52 msgid "Website name" msgstr "Nom du site" -#: preferences/forms.py:151 +#: preferences/forms.py:153 #: preferences/templates/preferences/display_preferences.html:54 msgid "Email address for automatic emailing" msgstr "Adresse mail pour les mails automatiques" -#: preferences/forms.py:152 +#: preferences/forms.py:154 #: preferences/templates/preferences/display_preferences.html:76 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: preferences/forms.py:153 +#: preferences/forms.py:155 #: preferences/templates/preferences/display_preferences.html:78 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: preferences/forms.py:166 +#: preferences/forms.py:168 msgid "Organisation name" msgstr "Nom de l'association" -#: preferences/forms.py:167 -#: preferences/templates/preferences/display_preferences.html:307 +#: preferences/forms.py:169 +#: preferences/templates/preferences/display_preferences.html:313 msgid "SIRET number" msgstr "Numéro SIRET" -#: preferences/forms.py:168 +#: preferences/forms.py:170 msgid "Address (line 1)" msgstr "Adresse (ligne 1)" -#: preferences/forms.py:169 +#: preferences/forms.py:171 msgid "Address (line 2)" msgstr "Adresse (ligne 2)" -#: preferences/forms.py:170 -#: preferences/templates/preferences/display_preferences.html:315 +#: preferences/forms.py:172 +#: preferences/templates/preferences/display_preferences.html:321 msgid "Contact email address" msgstr "Adresse mail de contact" -#: preferences/forms.py:171 -#: preferences/templates/preferences/display_preferences.html:319 +#: preferences/forms.py:173 +#: preferences/templates/preferences/display_preferences.html:325 msgid "Telephone number" msgstr "Numéro de téléphone" -#: preferences/forms.py:172 -#: preferences/templates/preferences/display_preferences.html:321 +#: preferences/forms.py:174 +#: preferences/templates/preferences/display_preferences.html:327 msgid "Usual name" msgstr "Nom d'usage" -#: preferences/forms.py:174 +#: preferences/forms.py:176 msgid "Account used for editing from /admin" msgstr "Compte utilisé pour les modifications depuis /admin" -#: preferences/forms.py:176 preferences/forms.py:321 +#: preferences/forms.py:178 preferences/forms.py:323 #: preferences/templates/preferences/aff_service.html:33 msgid "Description" msgstr "Description" -#: preferences/forms.py:190 +#: preferences/forms.py:192 msgid "Message for the French welcome email" msgstr "Message pour le mail de bienvenue en français" -#: preferences/forms.py:193 +#: preferences/forms.py:195 msgid "Message for the English welcome email" msgstr "Message pour le mail de bienvenue en anglais" -#: preferences/forms.py:207 +#: preferences/forms.py:209 msgid "Facebook URL" msgstr "URL du compte Facebook" -#: preferences/forms.py:208 +#: preferences/forms.py:210 msgid "Twitter URL" msgstr "URL du compte Twitter" -#: preferences/forms.py:209 -#: preferences/templates/preferences/display_preferences.html:482 +#: preferences/forms.py:211 +#: preferences/templates/preferences/display_preferences.html:488 msgid "Twitter account name" msgstr "Nom du compte Twitter" -#: preferences/forms.py:227 +#: preferences/forms.py:229 msgid "You chose to set vlan but did not set any VLAN." msgstr "" "Vous avez choisi de paramétrer vlan mais vous n'avez indiqué aucun VLAN." -#: preferences/forms.py:228 +#: preferences/forms.py:230 msgid "Please, choose a VLAN." msgstr "Veuillez choisir un VLAN." -#: preferences/forms.py:259 +#: preferences/forms.py:261 msgid "There is already a mandate taking place at the specified start date." msgstr "Il y a déjà un mandat ayant cours à la date de début renseignée." -#: preferences/forms.py:273 +#: preferences/forms.py:275 msgid "There is already a mandate taking place at the specified end date." msgstr "Il y a déjà un madant ayant cours à la date de fin renseignée." -#: preferences/forms.py:287 +#: preferences/forms.py:289 msgid "The specified dates overlap with an existing mandate." msgstr "Les dates renseignées se superposent avec un mandat existant." -#: preferences/forms.py:319 +#: preferences/forms.py:321 #: preferences/templates/preferences/aff_service.html:31 -#: preferences/templates/preferences/display_preferences.html:305 +#: preferences/templates/preferences/display_preferences.html:311 msgid "Name" msgstr "Nom" -#: preferences/forms.py:320 +#: preferences/forms.py:322 #: preferences/templates/preferences/aff_service.html:32 msgid "URL" msgstr "URL" -#: preferences/forms.py:322 +#: preferences/forms.py:324 #: preferences/templates/preferences/aff_service.html:34 msgid "Image" msgstr "Image" -#: preferences/forms.py:330 +#: preferences/forms.py:332 msgid "Current services" msgstr "Services actuels" -#: preferences/forms.py:419 +#: preferences/forms.py:421 msgid "Current email addresses" msgstr "Adresses mail actuelles" -#: preferences/forms.py:454 +#: preferences/forms.py:456 msgid "Current document templates" msgstr "Modèles de document actuels" -#: preferences/forms.py:484 +#: preferences/forms.py:486 msgid "Current attributes" msgstr "Attributs actuels" @@ -285,19 +291,19 @@ msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:111 +#: preferences/models.py:113 msgid "" -"Users with an email address not yet confirmed will be disabled after" -" this number of days." +"Users with an email address not yet confirmed will be disabled after this " +"number of days." msgstr "" -"Les utilisateurs n'ayant pas confirmé leur addresse mail seront" -" désactivés après ce nombre de jours" +"Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " +"après ce nombre de jours" -#: preferences/models.py:114 +#: preferences/models.py:117 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:116 +#: preferences/models.py:122 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -305,184 +311,183 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:116 +#: preferences/models.py:129 msgid "" -"If True, users have the choice to receive an email containing" -" a link to reset their password during creation, or to directly" -" set their password in the page." -" If False, an email is always sent." +"If True, users have the choice to receive an email containing a link to " +"reset their password during creation, or to directly set their password in " +"the page. If False, an email is always sent." msgstr "" -"Si True, les utilisateurs ont le choix de recevoir un email avec" -" un lien pour changer leur mot de passe lors de la création de leur compte, " -" ou alors de choisir leur mot de passe immédiatement." -" Si False, un mail est toujours envoyé." +"Si True, les utilisateurs ont le choix de recevoir un email avec un lien " +"pour changer leur mot de passe lors de la création de leur compte, ou alors " +"de choisir leur mot de passe immédiatement. Si False, un mail est toujours " +"envoyé." -#: preferences/models.py:121 +#: preferences/models.py:136 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:125 +#: preferences/models.py:140 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:126 +#: preferences/models.py:141 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:133 +#: preferences/models.py:148 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:151 +#: preferences/models.py:166 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:152 +#: preferences/models.py:167 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:153 +#: preferences/models.py:168 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:162 +#: preferences/models.py:177 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:172 +#: preferences/models.py:187 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:173 +#: preferences/models.py:188 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:193 preferences/models.py:651 +#: preferences/models.py:208 preferences/models.py:666 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:194 preferences/models.py:652 +#: preferences/models.py:209 preferences/models.py:667 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:200 +#: preferences/models.py:215 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:205 +#: preferences/models.py:220 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:211 +#: preferences/models.py:226 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:218 +#: preferences/models.py:233 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:224 +#: preferences/models.py:239 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:227 +#: preferences/models.py:242 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:230 +#: preferences/models.py:245 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:334 +#: preferences/models.py:349 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:335 +#: preferences/models.py:350 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:348 +#: preferences/models.py:363 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:350 +#: preferences/models.py:365 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:353 +#: preferences/models.py:368 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:357 +#: preferences/models.py:372 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:358 preferences/views.py:331 +#: preferences/models.py:373 preferences/views.py:331 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:359 -#: preferences/templates/preferences/display_preferences.html:201 +#: preferences/models.py:374 +#: preferences/templates/preferences/display_preferences.html:207 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:366 +#: preferences/models.py:381 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:369 +#: preferences/models.py:384 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:375 +#: preferences/models.py:390 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:376 +#: preferences/models.py:391 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:378 +#: preferences/models.py:393 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:385 +#: preferences/models.py:400 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:388 preferences/views.py:394 +#: preferences/models.py:403 preferences/views.py:394 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:391 +#: preferences/models.py:406 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:403 +#: preferences/models.py:418 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:409 +#: preferences/models.py:424 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:413 +#: preferences/models.py:428 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:414 preferences/views.py:276 +#: preferences/models.py:429 preferences/views.py:276 msgid "reminder" msgstr "rappel" -#: preferences/models.py:415 +#: preferences/models.py:430 msgid "reminders" msgstr "rappels" -#: preferences/models.py:436 +#: preferences/models.py:451 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -490,7 +495,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:444 +#: preferences/models.py:459 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -498,75 +503,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:459 +#: preferences/models.py:474 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:460 +#: preferences/models.py:475 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:480 +#: preferences/models.py:495 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:481 preferences/views.py:227 +#: preferences/models.py:496 preferences/views.py:227 msgid "service" msgstr "service" -#: preferences/models.py:482 +#: preferences/models.py:497 msgid "services" msgstr "services" -#: preferences/models.py:492 +#: preferences/models.py:507 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:498 +#: preferences/models.py:513 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:508 +#: preferences/models.py:523 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:510 +#: preferences/models.py:525 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:511 +#: preferences/models.py:526 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:519 preferences/views.py:634 +#: preferences/models.py:534 preferences/views.py:634 msgid "mandate" msgstr "mandat" -#: preferences/models.py:520 +#: preferences/models.py:535 msgid "mandates" msgstr "mandats" -#: preferences/models.py:521 +#: preferences/models.py:536 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:528 +#: preferences/models.py:543 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:529 +#: preferences/models.py:544 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:531 +#: preferences/models.py:546 msgid "start date" msgstr "date de début" -#: preferences/models.py:532 +#: preferences/models.py:547 msgid "end date" msgstr "date de fin" -#: preferences/models.py:545 +#: preferences/models.py:560 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -574,140 +579,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:560 +#: preferences/models.py:575 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:563 +#: preferences/models.py:578 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:564 +#: preferences/models.py:579 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:567 +#: preferences/models.py:582 msgid "Organisation" msgstr "Association" -#: preferences/models.py:574 +#: preferences/models.py:589 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:575 +#: preferences/models.py:590 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:593 +#: preferences/models.py:608 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:594 +#: preferences/models.py:609 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:608 +#: preferences/models.py:623 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:611 +#: preferences/models.py:626 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:616 +#: preferences/models.py:631 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:618 +#: preferences/models.py:633 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:623 +#: preferences/models.py:638 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:624 +#: preferences/models.py:639 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:628 preferences/views.py:587 +#: preferences/models.py:643 preferences/views.py:587 msgid "attribute" msgstr "attribut" -#: preferences/models.py:629 +#: preferences/models.py:644 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:631 +#: preferences/models.py:646 msgid "value" msgstr "valeur" -#: preferences/models.py:633 +#: preferences/models.py:648 msgid "comment" msgstr "commentaire" -#: preferences/models.py:634 +#: preferences/models.py:649 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:645 +#: preferences/models.py:660 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:646 -#: preferences/templates/preferences/display_preferences.html:279 +#: preferences/models.py:661 +#: preferences/templates/preferences/display_preferences.html:285 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:657 +#: preferences/models.py:672 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:658 +#: preferences/models.py:673 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:667 +#: preferences/models.py:682 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:675 +#: preferences/models.py:690 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:676 +#: preferences/models.py:691 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:682 +#: preferences/models.py:697 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:683 +#: preferences/models.py:698 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:689 +#: preferences/models.py:704 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:697 +#: preferences/models.py:712 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:698 +#: preferences/models.py:713 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:704 +#: preferences/models.py:719 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:705 +#: preferences/models.py:720 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:712 +#: preferences/models.py:727 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -715,87 +720,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:722 +#: preferences/models.py:737 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:723 +#: preferences/models.py:738 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:729 +#: preferences/models.py:744 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:730 +#: preferences/models.py:745 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:736 +#: preferences/models.py:751 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:744 +#: preferences/models.py:759 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:745 +#: preferences/models.py:760 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:751 +#: preferences/models.py:766 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:752 +#: preferences/models.py:767 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:758 +#: preferences/models.py:773 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:766 +#: preferences/models.py:781 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:767 +#: preferences/models.py:782 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:773 +#: preferences/models.py:788 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:774 +#: preferences/models.py:789 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:787 +#: preferences/models.py:802 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:788 +#: preferences/models.py:803 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:815 +#: preferences/models.py:830 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:819 +#: preferences/models.py:834 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:826 +#: preferences/models.py:841 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:832 +#: preferences/models.py:847 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:834 +#: preferences/models.py:849 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -803,19 +808,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:846 +#: preferences/models.py:861 msgid "template" msgstr "modèle" -#: preferences/models.py:847 +#: preferences/models.py:862 msgid "name" msgstr "nom" -#: preferences/models.py:850 +#: preferences/models.py:865 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:851 +#: preferences/models.py:866 msgid "document templates" msgstr "modèles de document" @@ -828,7 +833,7 @@ msgid "File" msgstr "Fichier" #: preferences/templates/preferences/aff_mailcontact.html:31 -#: preferences/templates/preferences/display_preferences.html:311 +#: preferences/templates/preferences/display_preferences.html:317 msgid "Address" msgstr "Adresse" @@ -1008,12 +1013,12 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:46 #: preferences/templates/preferences/display_preferences.html:108 -#: preferences/templates/preferences/display_preferences.html:167 -#: preferences/templates/preferences/display_preferences.html:220 -#: preferences/templates/preferences/display_preferences.html:282 -#: preferences/templates/preferences/display_preferences.html:300 -#: preferences/templates/preferences/display_preferences.html:397 -#: preferences/templates/preferences/display_preferences.html:475 +#: preferences/templates/preferences/display_preferences.html:173 +#: preferences/templates/preferences/display_preferences.html:226 +#: preferences/templates/preferences/display_preferences.html:288 +#: preferences/templates/preferences/display_preferences.html:306 +#: preferences/templates/preferences/display_preferences.html:403 +#: preferences/templates/preferences/display_preferences.html:481 #: preferences/templates/preferences/edit_preferences.html:46 #: preferences/views.py:212 preferences/views.py:261 preferences/views.py:307 #: preferences/views.py:367 preferences/views.py:431 preferences/views.py:496 @@ -1074,244 +1079,248 @@ msgstr "%(delete_notyetactive)s jours" msgid "All users are active by default" msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/templates/preferences/display_preferences.html:130 +#: preferences/templates/preferences/display_preferences.html:128 msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" -#: preferences/templates/preferences/display_preferences.html:133 +#: preferences/templates/preferences/display_preferences.html:132 msgid "Allow directly entering a password during account creation" -msgstr "Permettre le choix d'un mot de passe directement lors de la création du compte" +msgstr "" +"Permettre le choix d'un mot de passe directement lors de la création du " +"compte" -#: preferences/templates/preferences/display_preferences.html:136 -msgid "Delay before disabling accounts without a verified email" -msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" +#: preferences/templates/preferences/display_preferences.html:135 +#, fuzzy, python-format +#| msgid "%(delete_notyetactive)s days" +msgid "%(disable_emailnotyetconfirmed)s days" +msgstr "%(delete_notyetactive)s jours" -#: preferences/templates/preferences/display_preferences.html:136 +#: preferences/templates/preferences/display_preferences.html:139 msgid "Users general permissions" msgstr "Permissions générales des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:136 +#: preferences/templates/preferences/display_preferences.html:142 msgid "Default shell for users" msgstr "Interface en ligne de commande par défaut pour les utilisateurs" -#: preferences/templates/preferences/display_preferences.html:138 +#: preferences/templates/preferences/display_preferences.html:144 msgid "Users can edit their shell" msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" -#: preferences/templates/preferences/display_preferences.html:142 +#: preferences/templates/preferences/display_preferences.html:148 msgid "Users can edit their room" msgstr "Les utilisateurs peuvent modifier leur chambre" -#: preferences/templates/preferences/display_preferences.html:148 +#: preferences/templates/preferences/display_preferences.html:154 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" -#: preferences/templates/preferences/display_preferences.html:159 +#: preferences/templates/preferences/display_preferences.html:165 msgid "Machine preferences" msgstr "Préférences de machine" -#: preferences/templates/preferences/display_preferences.html:172 +#: preferences/templates/preferences/display_preferences.html:178 msgid "Password per machine" msgstr "Mot de passe par machine" -#: preferences/templates/preferences/display_preferences.html:180 +#: preferences/templates/preferences/display_preferences.html:186 msgid "Default Time To Live (TTL) for CNAME, A and AAAA records." msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA." -#: preferences/templates/preferences/display_preferences.html:184 +#: preferences/templates/preferences/display_preferences.html:190 msgid "IPv6 support" msgstr "Support de l'IPv6" -#: preferences/templates/preferences/display_preferences.html:186 +#: preferences/templates/preferences/display_preferences.html:192 msgid "Creation of machines" msgstr "Création de machines" -#: preferences/templates/preferences/display_preferences.html:196 +#: preferences/templates/preferences/display_preferences.html:202 msgid "Topology preferences" msgstr "Préférences de topologie" -#: preferences/templates/preferences/display_preferences.html:203 +#: preferences/templates/preferences/display_preferences.html:209 msgid "Add a RADIUS key" msgstr "Ajouter une clé RADIUS" -#: preferences/templates/preferences/display_preferences.html:213 +#: preferences/templates/preferences/display_preferences.html:219 msgid "Configuration of switches" msgstr "Configuration de commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:226 +#: preferences/templates/preferences/display_preferences.html:232 msgid "Web management, activated in case of automatic provision" msgstr "Gestion web, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:228 +#: preferences/templates/preferences/display_preferences.html:234 msgid "REST management, activated in case of automatic provision" msgstr "Gestion REST, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:235 +#: preferences/templates/preferences/display_preferences.html:241 msgid "Provision of configuration for switches" msgstr "Provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:238 +#: preferences/templates/preferences/display_preferences.html:244 msgid "Switches with automatic provision" msgstr "Commutateurs réseau avec provision automatique" -#: preferences/templates/preferences/display_preferences.html:239 -#: preferences/templates/preferences/display_preferences.html:243 -#: preferences/templates/preferences/display_preferences.html:247 -#: preferences/templates/preferences/display_preferences.html:255 -#: preferences/templates/preferences/display_preferences.html:259 -#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:245 +#: preferences/templates/preferences/display_preferences.html:249 +#: preferences/templates/preferences/display_preferences.html:253 +#: preferences/templates/preferences/display_preferences.html:261 +#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:275 msgid "OK" msgstr "OK" -#: preferences/templates/preferences/display_preferences.html:239 -#: preferences/templates/preferences/display_preferences.html:243 -#: preferences/templates/preferences/display_preferences.html:247 -#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:245 +#: preferences/templates/preferences/display_preferences.html:249 +#: preferences/templates/preferences/display_preferences.html:253 +#: preferences/templates/preferences/display_preferences.html:275 msgid "Missing" msgstr "Manquant" -#: preferences/templates/preferences/display_preferences.html:242 +#: preferences/templates/preferences/display_preferences.html:248 msgid "IP range for the management of switches" msgstr "Plage d'IP pour la gestion des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:246 +#: preferences/templates/preferences/display_preferences.html:252 msgid "Server for the configuration of switches" msgstr "Serveur pour la configuration des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:250 +#: preferences/templates/preferences/display_preferences.html:256 msgid "Provision of configuration mode for switches" msgstr "Mode de provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:254 +#: preferences/templates/preferences/display_preferences.html:260 msgid "TFTP mode" msgstr "Mode TFTP" -#: preferences/templates/preferences/display_preferences.html:258 +#: preferences/templates/preferences/display_preferences.html:264 msgid "SFTP mode" msgstr "Mode SFTP" -#: preferences/templates/preferences/display_preferences.html:259 +#: preferences/templates/preferences/display_preferences.html:265 msgid "Missing credentials" msgstr "Identifiants manquants" -#: preferences/templates/preferences/display_preferences.html:263 +#: preferences/templates/preferences/display_preferences.html:269 msgid "Switch management credentials" msgstr "Identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:271 msgid "Add switch management credentials" msgstr "Ajouter des identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:276 +#: preferences/templates/preferences/display_preferences.html:282 msgid "RADIUS preferences" msgstr "Préférences RADIUS" -#: preferences/templates/preferences/display_preferences.html:285 +#: preferences/templates/preferences/display_preferences.html:291 msgid "Current RADIUS attributes" msgstr "Attributs RADIUS actuels" -#: preferences/templates/preferences/display_preferences.html:286 +#: preferences/templates/preferences/display_preferences.html:292 msgid "Add an attribute" msgstr "Ajouter un attribut" -#: preferences/templates/preferences/display_preferences.html:294 +#: preferences/templates/preferences/display_preferences.html:300 msgid "Information about the organisation" msgstr "Informations sur l'association" -#: preferences/templates/preferences/display_preferences.html:325 +#: preferences/templates/preferences/display_preferences.html:331 msgid "User object of the organisation" msgstr "Objet utilisateur de l'association" -#: preferences/templates/preferences/display_preferences.html:327 +#: preferences/templates/preferences/display_preferences.html:333 msgid "Description of the organisation" msgstr "Description de l'association" -#: preferences/templates/preferences/display_preferences.html:331 +#: preferences/templates/preferences/display_preferences.html:337 msgid "Mandates" msgstr "Mandats" -#: preferences/templates/preferences/display_preferences.html:334 +#: preferences/templates/preferences/display_preferences.html:340 msgid "Add a mandate" msgstr "Ajouter un mandat" -#: preferences/templates/preferences/display_preferences.html:343 +#: preferences/templates/preferences/display_preferences.html:349 msgid "Document templates" msgstr "Modèles de document" -#: preferences/templates/preferences/display_preferences.html:349 +#: preferences/templates/preferences/display_preferences.html:355 msgid "Add a document template" msgstr "Ajouter un modèle de document" -#: preferences/templates/preferences/display_preferences.html:353 +#: preferences/templates/preferences/display_preferences.html:359 msgid "Delete one or several document templates" msgstr " Supprimer un ou plusieurs modèles de document" -#: preferences/templates/preferences/display_preferences.html:362 +#: preferences/templates/preferences/display_preferences.html:368 msgid "Subscription preferences" msgstr "Préférences de cotisation" -#: preferences/templates/preferences/display_preferences.html:371 +#: preferences/templates/preferences/display_preferences.html:377 msgid "Send voucher by email" msgstr "Envoyer le reçu par mail" -#: preferences/templates/preferences/display_preferences.html:375 +#: preferences/templates/preferences/display_preferences.html:381 msgid "Invoices' template" msgstr "Modèle des factures" -#: preferences/templates/preferences/display_preferences.html:379 +#: preferences/templates/preferences/display_preferences.html:385 msgid "Vouchers' template" msgstr "Modèle des reçus" -#: preferences/templates/preferences/display_preferences.html:390 +#: preferences/templates/preferences/display_preferences.html:396 msgid "Message for emails" msgstr "Message pour les mails" -#: preferences/templates/preferences/display_preferences.html:403 +#: preferences/templates/preferences/display_preferences.html:409 msgid "Welcome email (in French)" msgstr "Mail de bienvenue (en français)" -#: preferences/templates/preferences/display_preferences.html:407 +#: preferences/templates/preferences/display_preferences.html:413 msgid "Welcome email (in English)" msgstr "Mail de bienvenue (en anglais)" -#: preferences/templates/preferences/display_preferences.html:417 +#: preferences/templates/preferences/display_preferences.html:423 msgid "Preferences for the membership's end email" msgstr "Préférences pour le mail de fin d'adhésion" -#: preferences/templates/preferences/display_preferences.html:423 +#: preferences/templates/preferences/display_preferences.html:429 msgid "Add a reminder" msgstr "Ajouter un rappel" -#: preferences/templates/preferences/display_preferences.html:434 +#: preferences/templates/preferences/display_preferences.html:440 msgid "List of services and homepage preferences" msgstr "Liste des services et préférences de page d'accueil" -#: preferences/templates/preferences/display_preferences.html:440 +#: preferences/templates/preferences/display_preferences.html:446 msgid "Add a service" msgstr "Ajouter un service" -#: preferences/templates/preferences/display_preferences.html:451 +#: preferences/templates/preferences/display_preferences.html:457 msgid "List of contact email addresses" msgstr "Liste des adresses mail de contact" -#: preferences/templates/preferences/display_preferences.html:457 +#: preferences/templates/preferences/display_preferences.html:463 msgid "Add an address" msgstr "Ajouter une adresse" -#: preferences/templates/preferences/display_preferences.html:459 +#: preferences/templates/preferences/display_preferences.html:465 msgid "Delete one or several addresses" msgstr "Supprimer une ou plusieurs adresses" -#: preferences/templates/preferences/display_preferences.html:468 +#: preferences/templates/preferences/display_preferences.html:474 msgid "Social networks" msgstr "Réseaux sociaux" -#: preferences/templates/preferences/display_preferences.html:480 +#: preferences/templates/preferences/display_preferences.html:486 msgid "Twitter account URL" msgstr "URL du compte Twitter" -#: preferences/templates/preferences/display_preferences.html:486 +#: preferences/templates/preferences/display_preferences.html:492 msgid "Facebook account URL" msgstr "URL du compte Facebook" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 6f5dbd0b..05954f8f 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index e03da256..45226988 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -82,94 +82,81 @@ msgstr "Ports" msgid "Switches" msgstr "Commutateurs réseau" -#: search/forms.py:62 search/forms.py:77 search/templates/search/search.html:29 +#: search/forms.py:62 search/forms.py:78 search/templates/search/search.html:29 #: search/templates/search/search.html:48 msgid "Search" msgstr "Rechercher" -#: search/forms.py:65 search/forms.py:80 +#: search/forms.py:65 search/forms.py:81 msgid "" -"Use « » and «,» to specify distinct words, «\"query\"» for" -" an exact search, «\\» to escape a character and «+» to" -" combine keywords." +"Use « » and «,» to specify distinct words, «\"query\"» for an exact search, " +"«\\» to escape a character and «+» to combine keywords." msgstr "" -"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour" -" une recherche exacte, «\\» pour échapper un caractère et «+» pour" -" combiner des mots clés." +"Utilisez « » et «,» pour spécifier différents mots, «\"recherche\"» pour une " +"recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des " +"mots clés." -#: search/forms.py:88 +#: search/forms.py:90 msgid "Users filter" msgstr "Filtre utilisateurs" -#: search/forms.py:95 +#: search/forms.py:97 msgid "Display filter" msgstr "Filtre affichage" -#: search/forms.py:101 +#: search/forms.py:103 msgid "Start date" msgstr "Date de début" -#: search/forms.py:102 +#: search/forms.py:104 msgid "End date" msgstr "Date de fin" -#: search/models.py:195 -msgid "Verified" -msgstr "Confirmé" - -#: search/models.py:196 -msgid "Unverified" -msgstr "Non-confirmé" - -#: search/models.py:197 -msgid "Waiting for email confirmation" -msgstr "En attente de confirmation du mail" - #: search/templates/search/index.html:29 msgid "Search results" msgstr "Résultats de la recherche" -#: search/templates/search/index.html:33 +#: search/templates/search/index.html:34 msgid "Results among users:" msgstr "Résultats parmi les utilisateurs :" -#: search/templates/search/index.html:37 +#: search/templates/search/index.html:44 msgid "Results among clubs:" msgstr "Résultats parmi les clubs :" -#: search/templates/search/index.html:41 +#: search/templates/search/index.html:54 msgid "Results among machines:" msgstr "Résultats parmi les machines :" -#: search/templates/search/index.html:45 +#: search/templates/search/index.html:64 msgid "Results among invoices:" msgstr "Résultats parmi les factures :" -#: search/templates/search/index.html:49 +#: search/templates/search/index.html:74 msgid "Results among whitelists:" msgstr "Résultats parmi les accès à titre gracieux :" -#: search/templates/search/index.html:53 +#: search/templates/search/index.html:84 msgid "Results among bans:" msgstr "Résultats parmi les bannissements :" -#: search/templates/search/index.html:57 +#: search/templates/search/index.html:94 msgid "Results among rooms:" msgstr "Résultats parmi les chambres :" -#: search/templates/search/index.html:61 +#: search/templates/search/index.html:104 msgid "Results among ports:" msgstr "Résultats parmi les ports :" -#: search/templates/search/index.html:65 +#: search/templates/search/index.html:114 msgid "Results among switches:" msgstr "Résultats parmi les commutateurs réseau :" -#: search/templates/search/index.html:69 +#: search/templates/search/index.html:123 msgid "No result" msgstr "Pas de résultat" -#: search/templates/search/index.html:71 +#: search/templates/search/index.html:125 #, python-format msgid "Only the first %(max_result)s results are displayed in each category." msgstr "" @@ -183,3 +170,12 @@ msgstr "Recherche simple" #: search/templates/search/sidebar.html:35 msgid "Advanced search" msgstr "Recherche avancée" + +#~ msgid "Verified" +#~ msgstr "Confirmé" + +#~ msgid "Unverified" +#~ msgstr "Non-confirmé" + +#~ msgid "Waiting for email confirmation" +#~ msgstr "En attente de confirmation du mail" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 80131b1d..6aa9bf63 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -339,19 +339,19 @@ msgstr "Si vous n'avez aucune idée de ce que vous avez fait :" msgid "Go back to a safe page" msgstr "Retourner à une page sécurisée" -#: templates/pagination.html:34 +#: templates/pagination.html:35 msgid "First" msgstr "Première page" -#: templates/pagination.html:40 +#: templates/pagination.html:41 msgid "Previous" msgstr "Précédent" -#: templates/pagination.html:60 +#: templates/pagination.html:61 msgid "Next" msgstr "Suivant" -#: templates/pagination.html:66 +#: templates/pagination.html:67 msgid "Last" msgstr "Dernière page" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 1ccfe87f..b562c90c 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index ef0afb98..4f4d8217 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-20 01:24+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index e2a8f3ce..6d22504c 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-11-19 23:43+0100\n" +"POT-Creation-Date: 2020-04-18 00:48+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,151 +35,150 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: users/forms.py:78 +#: users/forms.py:80 msgid "Current password" msgstr "Mot de passe actuel" -#: users/forms.py:81 users/forms.py:528 users/forms.py:556 +#: users/forms.py:83 users/forms.py:613 users/forms.py:641 msgid "New password" msgstr "Nouveau mot de passe" -#: users/forms.py:87 +#: users/forms.py:89 msgid "New password confirmation" msgstr "Confirmation du nouveau mot de passe" -#: users/forms.py:103 +#: users/forms.py:105 msgid "The new passwords don't match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: users/forms.py:109 +#: users/forms.py:111 msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: users/forms.py:129 users/forms.py:186 users/models.py:1760 +#: users/forms.py:132 users/forms.py:189 users/forms.py:410 +#: users/models.py:1893 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:135 users/forms.py:189 +#: users/forms.py:138 users/forms.py:192 users/forms.py:417 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: users/forms.py:140 users/forms.py:229 +#: users/forms.py:143 users/forms.py:232 msgid "Is admin" msgstr "Est admin" -#: users/forms.py:153 +#: users/forms.py:156 msgid "You can't use an internal address as your external address." msgstr "" "Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe." -#: users/forms.py:166 users/forms.py:209 +#: users/forms.py:169 users/forms.py:212 users/forms.py:491 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: users/forms.py:238 +#: users/forms.py:241 #, python-format msgid "User is admin: %s" msgstr "L'utilisateur est admin : %s" -#: users/forms.py:284 users/templates/users/aff_clubs.html:38 +#: users/forms.py:287 users/templates/users/aff_clubs.html:38 #: users/templates/users/aff_listright.html:63 #: users/templates/users/aff_listright.html:168 #: users/templates/users/aff_users.html:39 -#: users/templates/users/profil.html:178 users/templates/users/profil.html:342 -#: users/templates/users/profil.html:361 +#: users/templates/users/profil.html:204 users/templates/users/profil.html:368 +#: users/templates/users/profil.html:387 msgid "Username" msgstr "Pseudo" -#: users/forms.py:296 +#: users/forms.py:299 msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" msgstr "" "Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" -#: users/forms.py:309 +#: users/forms.py:312 msgid "Impossible to archive users whose end access date is in the future." msgstr "" "Impossible d'archiver des utilisateurs dont la date de fin de connexion est " "dans le futur." -#: users/forms.py:324 users/templates/users/profil.html:167 -#: users/templates/users/profil.html:341 users/templates/users/profil.html:360 +#: users/forms.py:327 users/templates/users/aff_users.html:35 +#: users/templates/users/profil.html:193 users/templates/users/profil.html:367 +#: users/templates/users/profil.html:386 msgid "First name" msgstr "Prénom" -#: users/forms.py:325 users/templates/users/aff_users.html:37 -#: users/templates/users/profil.html:173 users/templates/users/profil.html:340 -#: users/templates/users/profil.html:359 +#: users/forms.py:328 users/templates/users/aff_users.html:37 +#: users/templates/users/profil.html:199 users/templates/users/profil.html:366 +#: users/templates/users/profil.html:385 msgid "Surname" msgstr "Nom" -#: users/forms.py:326 users/forms.py:466 users/models.py:1760 +#: users/forms.py:329 users/forms.py:551 users/models.py:1893 #: users/templates/users/aff_emailaddress.html:36 -#: users/templates/users/profil.html:183 +#: users/templates/users/profil.html:209 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:327 users/forms.py:464 users/forms.py:614 +#: users/forms.py:330 users/forms.py:549 users/forms.py:694 #: users/templates/users/aff_schools.html:37 -#: users/templates/users/profil.html:204 +#: users/templates/users/profil.html:230 msgid "School" msgstr "Établissement" -#: users/forms.py:328 users/forms.py:465 +#: users/forms.py:331 users/forms.py:550 #: users/templates/users/aff_serviceusers.html:34 -#: users/templates/users/profil.html:209 +#: users/templates/users/profil.html:235 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:330 users/forms.py:468 +#: users/forms.py:333 users/forms.py:553 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 -#: users/templates/users/profil.html:188 +#: users/templates/users/profil.html:214 msgid "Room" msgstr "Chambre" -#: users/forms.py:331 users/forms.py:469 +#: users/forms.py:334 users/forms.py:554 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:332 users/forms.py:470 +#: users/forms.py:335 users/forms.py:555 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: users/forms.py:348 +#: users/forms.py:351 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:358 users/forms.py:765 +#: users/forms.py:361 users/forms.py:845 msgid "You can't use a {} address." msgstr "Vous ne pouvez pas utiliser une adresse {}." -#: users/forms.py:368 users/forms.py:493 +#: users/forms.py:371 users/forms.py:578 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:404 +#: users/forms.py:390 msgid "" -"If this options is set, you will receive a link to set" -" your initial password by email. If you do not have" -" any means of accessing your emails, you can disable" -" this option to set your password immediatly." -" You will still receive an email to confirm your address." -" Failure to confirm your address will result in an" -" automatic suspension of your account until you do." +"If this options is set, you will receive a link to set your initial password " +"by email. If you do not have any means of accessing your emails, you can " +"disable this option to set your password immediatly. You will still receive " +"an email to confirm your address. Failure to confirm your address will " +"result in an automatic suspension of your account until you do." msgstr "" -"Si cette option est activée, vous recevrez un lien pour choisir" -" votre mot de passe par email. Si vous n'avez pas accès" -" à vos mails, vous pouvez désactiver cette option" -" pour immédiatement entrer votre mot de passe." -" Vous recevrez tous de même un email de confirmation." -" Si vous ne confirmez pas votre adresse dans les délais" -" impartis, votre connexion sera automatiquement suspendue." +"Si cette option est activée, vous recevrez un lien pour choisir votre mot de " +"passe par email. Si vous n'avez pas accès à vos mails, vous pouvez " +"désactiver cette option pour immédiatement entrer votre mot de passe. Vous " +"recevrez tous de même un email de confirmation. Si vous ne confirmez pas " +"votre adresse dans les délais impartis, votre connexion sera automatiquement " +"suspendue." -#: users/forms.py:442 +#: users/forms.py:404 msgid "Send password reset link by email." msgstr "Envoyer le lien de modification du mot de passe par mail." -#: users/forms.py:435 +#: users/forms.py:425 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -190,239 +189,255 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:394 +#: users/forms.py:432 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:419 +#: users/forms.py:457 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:421 +#: users/forms.py:459 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:433 -msgid "Leave empty if you don't have any GPG key." -msgstr "Laissez vide si vous n'avez pas de clé GPG." - -#: users/forms.py:436 -msgid "Default shell" -msgstr "Interface en ligne de commande par défaut" - -#: users/forms.py:166 users/forms.py:481 +#: users/forms.py:477 msgid "Password must contain at least 8 characters." msgstr "Le mot de passe doit contenir au moins 8 caractères." -#: users/forms.py:463 users/templates/users/aff_clubs.html:36 +#: users/forms.py:518 +msgid "Leave empty if you don't have any GPG key." +msgstr "Laissez vide si vous n'avez pas de clé GPG." + +#: users/forms.py:521 +msgid "Default shell" +msgstr "Interface en ligne de commande par défaut" + +#: users/forms.py:548 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:471 +#: users/forms.py:556 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" +#: users/forms.py:662 users/templates/users/profil.html:277 +msgid "State" +msgstr "État" + #: users/forms.py:663 msgid "Email state" msgstr "État du mail" -#: users/forms.py:601 users/templates/users/aff_listright.html:38 +#: users/forms.py:681 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:627 +#: users/forms.py:707 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:647 +#: users/forms.py:727 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:659 +#: users/forms.py:739 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:668 +#: users/forms.py:748 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:686 +#: users/forms.py:766 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:705 users/forms.py:720 users/templates/users/aff_bans.html:41 +#: users/forms.py:785 users/forms.py:800 users/templates/users/aff_bans.html:41 #: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: users/forms.py:735 +#: users/forms.py:815 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:736 +#: users/forms.py:816 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:752 +#: users/forms.py:832 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:754 +#: users/forms.py:834 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:756 +#: users/forms.py:836 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:808 +#: users/forms.py:888 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:813 +#: users/forms.py:893 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" -#: users/models.py:105 +#: users/models.py:108 #, python-format msgid "The username \"%(label)s\" contains forbidden characters." msgstr "Le pseudo « %(label)s » contient des caractères interdits." -#: users/models.py:134 +#: users/models.py:137 msgid "Users must have an username." msgstr "Les utilisateurs doivent avoir un pseudo." -#: users/models.py:137 +#: users/models.py:140 msgid "Username should only contain [a-z0-9-]." msgstr "Le pseudo devrait seulement contenir [a-z0-9-]" -#: users/models.py:180 users/templates/users/aff_clubs.html:55 +#: users/models.py:184 users/templates/users/aff_clubs.html:55 #: users/templates/users/aff_users.html:57 -#: users/templates/users/profil.html:253 +#: users/templates/users/profil.html:279 msgid "Active" msgstr "Actif" -#: users/models.py:181 users/templates/users/aff_clubs.html:57 +#: users/models.py:185 users/templates/users/aff_clubs.html:57 #: users/templates/users/aff_users.html:59 -#: users/templates/users/profil.html:255 users/templates/users/profil.html:271 +#: users/templates/users/profil.html:281 users/templates/users/profil.html:297 msgid "Disabled" msgstr "Désactivé" -#: users/models.py:182 users/templates/users/profil.html:257 +#: users/models.py:186 users/templates/users/profil.html:283 msgid "Archived" msgstr "Archivé" -#: users/models.py:183 users/templates/users/profil.html:259 +#: users/models.py:187 users/templates/users/profil.html:285 msgid "Not yet active" msgstr "Pas encore adhéré" -#: users/models.py:184 users/templates/users/profil.html:261 +#: users/models.py:188 users/templates/users/profil.html:287 msgid "Fully archived" msgstr "Complètement archivé" -#: users/models.py:191 users/models.py:1416 +#: users/models.py:195 +msgid "Confirmed" +msgstr "Confirmé" + +#: users/models.py:196 +msgid "Not confirmed" +msgstr "Non confirmé" + +#: users/models.py:197 +msgid "Waiting for email confirmation" +msgstr "En attente de confirmation" + +#: users/models.py:204 users/models.py:1549 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." -#: users/models.py:197 +#: users/models.py:210 msgid "External email address allowing us to contact you." msgstr "Adresse mail externe nous permettant de vous contacter." -#: users/models.py:202 +#: users/models.py:215 msgid "" "Enable redirection of the local email messages to the main email address." msgstr "" "Activer la redirection des mails locaux vers l'adresse mail principale." -#: users/models.py:207 +#: users/models.py:220 msgid "Enable the local email account." msgstr "Activer le compte mail local" -#: users/models.py:216 +#: users/models.py:229 msgid "Comment, school year." msgstr "Commentaire, promotion." -#: users/models.py:225 +#: users/models.py:239 msgid "enable shortcuts on Re2o website" msgstr "activer les raccourcis sur le site de Re2o" -#: users/models.py:235 +#: users/models.py:250 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: users/models.py:236 +#: users/models.py:251 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: users/models.py:237 +#: users/models.py:252 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: users/models.py:238 +#: users/models.py:253 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:241 +#: users/models.py:256 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:243 +#: users/models.py:258 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:244 +#: users/models.py:259 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:246 +#: users/models.py:261 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:247 +#: users/models.py:262 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:265 users/models.py:293 +#: users/models.py:280 users/models.py:308 users/models.py:318 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:289 users/templates/users/aff_listright.html:75 +#: users/models.py:314 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:291 +#: users/models.py:316 msgid "Club" msgstr "Club" -#: users/models.py:781 +#: users/models.py:891 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:783 +#: users/models.py:893 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:806 users/templates/users/user_autocapture.html:64 +#: users/models.py:916 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: users/models.py:878 +#: users/models.py:1010 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:892 users/models.py:946 +#: users/models.py:1024 users/models.py:1078 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:904 +#: users/models.py:1036 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:911 +#: users/models.py:1043 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -430,61 +445,61 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:923 users/models.py:961 +#: users/models.py:1055 users/models.py:1093 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:987 +#: users/models.py:1119 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1004 +#: users/models.py:1136 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1024 +#: users/models.py:1156 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1041 users/models.py:1056 +#: users/models.py:1173 users/models.py:1188 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1071 +#: users/models.py:1203 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1086 +#: users/models.py:1218 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1102 +#: users/models.py:1234 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1127 +#: users/models.py:1259 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1136 +#: users/models.py:1268 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1151 users/models.py:1354 +#: users/models.py:1283 users/models.py:1487 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1168 +#: users/models.py:1300 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1190 +#: users/models.py:1323 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1198 +#: users/models.py:1331 msgid "" "There is neither a local email address nor an external email address for " "this user." @@ -492,7 +507,7 @@ msgstr "" "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " "utilisateur." -#: users/models.py:1205 +#: users/models.py:1338 msgid "" "You can't redirect your local emails if no external email address has been " "set." @@ -500,195 +515,195 @@ msgstr "" "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail externe " "n'a été définie." -#: users/models.py:1225 +#: users/models.py:1358 msgid "member" msgstr "adhérent" -#: users/models.py:1226 +#: users/models.py:1359 msgid "members" msgstr "adhérents" -#: users/models.py:1243 +#: users/models.py:1376 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1267 +#: users/models.py:1400 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1277 +#: users/models.py:1410 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1307 +#: users/models.py:1440 msgid "club" msgstr "club" -#: users/models.py:1308 +#: users/models.py:1441 msgid "clubs" msgstr "clubs" -#: users/models.py:1319 +#: users/models.py:1452 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1327 +#: users/models.py:1460 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1420 +#: users/models.py:1553 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1426 +#: users/models.py:1559 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1427 users/views.py:332 +#: users/models.py:1560 users/views.py:356 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1428 +#: users/models.py:1561 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1432 +#: users/models.py:1565 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1499 +#: users/models.py:1632 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1500 +#: users/models.py:1633 msgid "school" msgstr "établissement" -#: users/models.py:1501 +#: users/models.py:1634 msgid "schools" msgstr "établissements" -#: users/models.py:1520 +#: users/models.py:1653 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1526 +#: users/models.py:1659 msgid "Description." msgstr "Description." -#: users/models.py:1529 +#: users/models.py:1662 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1530 +#: users/models.py:1663 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1531 +#: users/models.py:1664 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1576 +#: users/models.py:1709 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1577 users/views.py:643 +#: users/models.py:1710 users/views.py:671 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1578 +#: users/models.py:1711 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1596 +#: users/models.py:1729 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1597 +#: users/models.py:1730 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1598 +#: users/models.py:1731 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1608 +#: users/models.py:1741 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1609 users/views.py:383 +#: users/models.py:1742 users/views.py:407 msgid "ban" msgstr "bannissement" -#: users/models.py:1610 +#: users/models.py:1743 msgid "bans" msgstr "bannissements" -#: users/models.py:1645 +#: users/models.py:1778 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1693 +#: users/models.py:1826 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1694 +#: users/models.py:1827 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1695 +#: users/models.py:1828 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1715 +#: users/models.py:1848 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:1912 +#: users/models.py:2046 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:1915 +#: users/models.py:2049 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:1920 +#: users/models.py:2054 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:1922 +#: users/models.py:2056 msgid "local email account" msgstr "compte mail local" -#: users/models.py:1923 +#: users/models.py:2057 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:1951 users/models.py:1986 users/models.py:2020 -#: users/models.py:2054 +#: users/models.py:2085 users/models.py:2120 users/models.py:2154 +#: users/models.py:2188 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:1956 +#: users/models.py:2090 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:1966 +#: users/models.py:2100 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:1992 +#: users/models.py:2126 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2012 +#: users/models.py:2146 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -696,13 +711,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2026 +#: users/models.py:2160 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2046 +#: users/models.py:2180 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -710,13 +725,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2060 +#: users/models.py:2194 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2069 +#: users/models.py:2203 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." @@ -742,18 +757,18 @@ msgstr "Fin de cotisation le" #: users/templates/users/aff_clubs.html:43 #: users/templates/users/aff_users.html:44 -#: users/templates/users/profil.html:266 +#: users/templates/users/profil.html:292 msgid "Internet access" msgstr "Accès Internet" #: users/templates/users/aff_clubs.html:44 -#: users/templates/users/aff_users.html:45 users/templates/users/profil.html:31 +#: users/templates/users/aff_users.html:45 users/templates/users/profil.html:33 msgid "Profile" msgstr "Profil" #: users/templates/users/aff_clubs.html:53 #: users/templates/users/aff_users.html:54 -#: users/templates/users/profil.html:228 +#: users/templates/users/profil.html:254 msgid "Not a member" msgstr "Non adhérent" @@ -851,10 +866,33 @@ msgid "Access group" msgstr "Group d'accès" #: users/templates/users/aff_shell.html:32 -#: users/templates/users/profil.html:307 +#: users/templates/users/profil.html:333 msgid "Shell" msgstr "Interface en ligne de commande" +#: users/templates/users/confirm_email.html:31 +#: users/templates/users/confirm_email.html:37 +#: users/templates/users/resend_confirmation_email.html:27 +msgid "Confirmation email" +msgstr "Email de confirmation" + +#: users/templates/users/confirm_email.html:38 +msgid "Confirm the email" +msgstr "Confirmer le mail" + +#: users/templates/users/confirm_email.html:38 +#, python-format +msgid "for %(name)s." +msgstr "pour %(name)s." + +#: users/templates/users/confirm_email.html:39 +#: users/templates/users/delete.html:36 +#: users/templates/users/mass_archive.html:36 +#: users/templates/users/resend_confirmation_email.html:35 users/views.py:628 +#: users/views.py:732 +msgid "Confirm" +msgstr "Confirmer" + #: users/templates/users/delete.html:29 msgid "Deletion of users" msgstr "Suppression d'utilisateurs" @@ -868,14 +906,8 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: users/templates/users/delete.html:36 -#: users/templates/users/mass_archive.html:36 users/views.py:600 -#: users/views.py:704 -msgid "Confirm" -msgstr "Confirmer" - #: users/templates/users/index_ban.html:32 -#: users/templates/users/profil.html:438 users/templates/users/sidebar.html:58 +#: users/templates/users/profil.html:464 users/templates/users/sidebar.html:58 msgid "Bans" msgstr "Bannissements" @@ -946,7 +978,7 @@ msgid "Add a shell" msgstr "Ajouter une interface en ligne de commande" #: users/templates/users/index_whitelist.html:32 -#: users/templates/users/profil.html:463 users/templates/users/sidebar.html:64 +#: users/templates/users/profil.html:489 users/templates/users/sidebar.html:64 msgid "Whitelists" msgstr "Accès gracieux" @@ -971,275 +1003,254 @@ msgstr "" "débrancher et rebrancher votre câble Ethernet pour bénéficier d'une " "connexion filaire." -#: users/templates/users/confirm_email.html:35 -#, python-format -msgid "Confirmation email" -msgstr "Email de confirmation" - -#: users/templates/users/confirm_email.html:36 -#, python-format -msgid "Confirm the email" -msgstr "Confirmer le mail" - -#: users/templates/users/confirm_email.html:36 -#, python-format -msgid "for %(name)s." -msgstr "pour %(name)s." - -#: users/templates/users/profil.html:36 +#: users/templates/users/profil.html:38 #, python-format msgid "Welcome %(name)s %(surname)s" msgstr "Bienvenue %(name)s %(surname)s" -#: users/templates/users/profil.html:38 +#: users/templates/users/profil.html:40 #, python-format msgid "Profile of %(name)s %(surname)s" msgstr "Profil de %(name)s %(surname)s" -#: users/templates/users/profil.html:43 +#: users/templates/users/profil.html:47 #, python-format msgid "" -"Please confirm your email address before %(confirm_before_date)s," -" or your account will be suspended." +"Please confirm your email address before %(confirm_before_date)s, or your " +"account will be suspended." msgstr "" -"Veuillez confirmer votre adresse mail avant le %(confirm_before_date)s," -" sous peine de suspension de votre compte." +"Veuillez confirmer votre adresse mail avant le %(confirm_before_date)s, sous " +"peine de suspension de votre compte." -#: users/templates/users/profil.html:48 -#, python-format +#: users/templates/users/profil.html:50 msgid "Didn't receive the email?" msgstr "Vous n'avez pas reçu le mail ?" -#: users/templates/users/profil.html:53 -#, python-format +#: users/templates/users/profil.html:55 msgid "Your account has been suspended, please confirm your email address." msgstr "Votre compte a été suspendu, veuillez confirmer votre adresse mail." -#: users/templates/users/profil.html:46 +#: users/templates/users/profil.html:65 msgid "Your account has been banned." msgstr "Votre compte a été banni." -#: users/templates/users/profil.html:48 +#: users/templates/users/profil.html:67 #, python-format msgid "End of the ban: %(end_ban_date)s" msgstr "Fin du bannissement : %(end_ban_date)s" -#: users/templates/users/profil.html:53 +#: users/templates/users/profil.html:72 msgid "No connection" msgstr "Pas de connexion" -#: users/templates/users/profil.html:57 -msgid "Pay for a connection" -msgstr "Payer une connexion" - -#: users/templates/users/profil.html:81 +#: users/templates/users/profil.html:77 msgid "Resend the email" msgstr "Renvoyer le mail" -#: users/templates/users/profil.html:60 +#: users/templates/users/profil.html:82 +msgid "Pay for a connection" +msgstr "Payer une connexion" + +#: users/templates/users/profil.html:85 msgid "Ask someone with the appropriate rights to pay for a connection." msgstr "" "Demandez à quelqu'un ayant les droits appropriés de payer une connexion." -#: users/templates/users/profil.html:66 +#: users/templates/users/profil.html:92 #, python-format msgid "Connection (until %(end_connection_date)s )" msgstr "Connexion (jusqu'au %(end_connection_date)s)" -#: users/templates/users/profil.html:70 +#: users/templates/users/profil.html:96 msgid "Extend the connection period" msgstr "Étendre la durée de connexion" -#: users/templates/users/profil.html:86 +#: users/templates/users/profil.html:112 msgid "Refill the balance" msgstr "Recharger le solde" -#: users/templates/users/profil.html:97 users/templates/users/profil.html:382 +#: users/templates/users/profil.html:123 users/templates/users/profil.html:408 msgid "Machines" msgstr "Machines" -#: users/templates/users/profil.html:101 users/templates/users/profil.html:113 -#: users/templates/users/profil.html:390 +#: users/templates/users/profil.html:127 users/templates/users/profil.html:139 +#: users/templates/users/profil.html:416 msgid "Add a machine" msgstr "Ajouter une machine" -#: users/templates/users/profil.html:109 users/templates/users/profil.html:397 +#: users/templates/users/profil.html:135 users/templates/users/profil.html:423 msgid "No machine" msgstr "Pas de machine" -#: users/templates/users/profil.html:128 +#: users/templates/users/profil.html:154 msgid "Detailed information" msgstr "Informations détaillées" -#: users/templates/users/profil.html:135 users/views.py:184 users/views.py:211 -#: users/views.py:228 users/views.py:245 users/views.py:317 users/views.py:371 -#: users/views.py:425 users/views.py:490 users/views.py:533 users/views.py:569 -#: users/views.py:629 users/views.py:675 +#: users/templates/users/profil.html:161 users/views.py:202 users/views.py:233 +#: users/views.py:252 users/views.py:269 users/views.py:341 users/views.py:395 +#: users/views.py:449 users/views.py:514 users/views.py:561 users/views.py:597 +#: users/views.py:657 users/views.py:703 msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:139 users/views.py:264 users/views.py:1001 +#: users/templates/users/profil.html:165 users/views.py:288 users/views.py:1034 msgid "Change the password" msgstr "Changer le mot de passe" -#: users/templates/users/profil.html:144 +#: users/templates/users/profil.html:170 msgid "Change the state" msgstr "Changer l'état" -#: users/templates/users/profil.html:150 +#: users/templates/users/profil.html:176 msgid "Edit the groups" msgstr "Modifier les groupes" -#: users/templates/users/profil.html:160 +#: users/templates/users/profil.html:186 msgid "Mailing" msgstr "Envoi de mails" -#: users/templates/users/profil.html:164 +#: users/templates/users/profil.html:190 msgid "Mailing disabled" msgstr "Envoi de mails désactivé" -#: users/templates/users/profil.html:192 -msgid "Connected" -msgstr "Connecté" - -#: users/templates/users/profil.html:201 +#: users/templates/users/profil.html:210 msgid "Pending confirmation..." msgstr "En attente de confirmation..." -#: users/templates/users/profil.html:193 +#: users/templates/users/profil.html:218 +msgid "Connected" +msgstr "Connecté" + +#: users/templates/users/profil.html:219 msgid "Pending connection..." msgstr "Connexion en attente..." -#: users/templates/users/profil.html:199 +#: users/templates/users/profil.html:225 msgid "Telephone number" msgstr "Numéro de téléphone" -#: users/templates/users/profil.html:214 +#: users/templates/users/profil.html:240 msgid "Registration date" msgstr "Date d'inscription" -#: users/templates/users/profil.html:219 +#: users/templates/users/profil.html:245 msgid "Last login" msgstr "Dernière connexion" -#: users/templates/users/profil.html:224 +#: users/templates/users/profil.html:250 msgid "End of membership" msgstr "Fin d'adhésion" -#: users/templates/users/profil.html:233 +#: users/templates/users/profil.html:259 msgid "Whitelist" msgstr "Accès gracieux" -#: users/templates/users/profil.html:237 users/templates/users/profil.html:280 +#: users/templates/users/profil.html:263 users/templates/users/profil.html:306 msgid "None" msgstr "Aucun" -#: users/templates/users/profil.html:242 +#: users/templates/users/profil.html:268 msgid "Ban" msgstr "Bannissement" -#: users/templates/users/profil.html:246 +#: users/templates/users/profil.html:272 msgid "Not banned" msgstr "Non banni" -#: users/templates/users/profil.html:251 users/forms.py:662 -msgid "State" -msgstr "État" - -#: users/templates/users/profil.html:269 +#: users/templates/users/profil.html:295 #, python-format msgid "Active (until %(end_access)s)" msgstr "Actif (jusqu'au %(end_access)s)" -#: users/templates/users/profil.html:276 users/templates/users/sidebar.html:82 +#: users/templates/users/profil.html:302 users/templates/users/sidebar.html:82 msgid "Groups of rights" msgstr "Groupes de droits" -#: users/templates/users/profil.html:285 +#: users/templates/users/profil.html:311 msgid "Balance" msgstr "Solde" -#: users/templates/users/profil.html:292 +#: users/templates/users/profil.html:318 msgid "Refill" msgstr "Recharger" -#: users/templates/users/profil.html:300 +#: users/templates/users/profil.html:326 msgid "GPG fingerprint" msgstr "Empreinte GPG" -#: users/templates/users/profil.html:312 +#: users/templates/users/profil.html:338 msgid "Shortcuts enabled" msgstr "Raccourcis activés" -#: users/templates/users/profil.html:323 +#: users/templates/users/profil.html:349 msgid "Manage the club" msgstr "Gérer le club" -#: users/templates/users/profil.html:331 +#: users/templates/users/profil.html:357 msgid "Manage the admins and members" msgstr "Gérer les admins et les membres" -#: users/templates/users/profil.html:335 +#: users/templates/users/profil.html:361 msgid "Club admins" msgstr "Admins du clubs" -#: users/templates/users/profil.html:354 +#: users/templates/users/profil.html:380 msgid "Members" msgstr "Adhérents" -#: users/templates/users/profil.html:407 +#: users/templates/users/profil.html:433 msgid "Subscriptions" msgstr "Cotisations" -#: users/templates/users/profil.html:415 +#: users/templates/users/profil.html:441 msgid "Add a subscription" msgstr "Ajouter une cotisation" -#: users/templates/users/profil.html:420 +#: users/templates/users/profil.html:446 msgid "Edit the balance" msgstr "Modifier le solde" -#: users/templates/users/profil.html:429 +#: users/templates/users/profil.html:455 msgid "No invoice" msgstr "Pas de facture" -#: users/templates/users/profil.html:446 +#: users/templates/users/profil.html:472 msgid "Add a ban" msgstr "Ajouter un bannissement" -#: users/templates/users/profil.html:454 +#: users/templates/users/profil.html:480 msgid "No ban" msgstr "Pas de bannissement" -#: users/templates/users/profil.html:471 +#: users/templates/users/profil.html:497 msgid "Grant a whitelist" msgstr "Donner un accès gracieux" -#: users/templates/users/profil.html:479 +#: users/templates/users/profil.html:505 msgid "No whitelist" msgstr "Pas d'accès gracieux" -#: users/templates/users/profil.html:487 +#: users/templates/users/profil.html:513 msgid "Email settings" msgstr "Paramètres mail" -#: users/templates/users/profil.html:494 +#: users/templates/users/profil.html:520 msgid "Edit email settings" msgstr "Modifier les paramètres mail" -#: users/templates/users/profil.html:503 users/templates/users/profil.html:529 +#: users/templates/users/profil.html:529 users/templates/users/profil.html:555 msgid "Contact email address" msgstr "Adresse mail de contact" -#: users/templates/users/profil.html:507 +#: users/templates/users/profil.html:533 msgid "Enable the local email account" msgstr "Activer le compte mail local" -#: users/templates/users/profil.html:509 +#: users/templates/users/profil.html:535 msgid "Enable the local email redirection" msgstr "Activer la redirection mail locale" -#: users/templates/users/profil.html:513 +#: users/templates/users/profil.html:539 msgid "" "The contact email address is the email address to which we send emails to " "contact you. If you would like to use your external email address for that, " @@ -1251,17 +1262,15 @@ msgstr "" "externe pour cela, vous pouvez soit désactiver votre adresse mail locale " "soit activer la redirection des mails locaux." -#: users/templates/users/profil.html:518 +#: users/templates/users/profil.html:544 msgid "Add an email address" msgstr "Ajouter une adresse mail" -#: users/templates/users/resend_confirmation_email.html:35 -#, python-format +#: users/templates/users/resend_confirmation_email.html:33 msgid "Re-send confirmation email?" msgstr "Renvoyer l'email de confirmation ?" -#: users/templates/users/resend_confirmation_email.html:36 -#, python-format +#: users/templates/users/resend_confirmation_email.html:34 msgid "The confirmation email will be sent to" msgstr "Le mail de confirmation sera envoyé à" @@ -1324,38 +1333,37 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: users/views.py:131 +#: users/views.py:133 #, python-format msgid "The user %s was created, a confirmation email was sent." -msgstr "" -"L'utilisateur %s a été créé, un mail de confirmation a été envoyé." +msgstr "L'utilisateur %s a été créé, un mail de confirmation a été envoyé." -#: users/views.py:130 +#: users/views.py:140 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: users/views.py:137 +#: users/views.py:153 msgid "Commit" msgstr "Valider" -#: users/views.py:156 +#: users/views.py:174 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: users/views.py:161 +#: users/views.py:179 msgid "Create a club" msgstr "Créer un club" -#: users/views.py:176 +#: users/views.py:194 msgid "The club was edited." msgstr "Le club a été modifié." -#: users/views.py:208 +#: users/views.py:226 msgid "The user was edited." msgstr "L'utilisateur a été modifié." @@ -1363,118 +1371,122 @@ msgstr "L'utilisateur a été modifié." msgid "Sent a new confirmation email." msgstr "Un nouveau mail de confirmation a été envoyé." -#: users/views.py:225 +#: users/views.py:247 msgid "The states were edited." msgstr "Les états ont été modifié." -#: users/views.py:242 -msgid "The groups were edited." -msgstr "Les groupes ont été modifiés." - #: users/views.py:249 msgid "An email to confirm the address was sent." msgstr "Un mail pour confirmer l'adresse a été envoyé." -#: users/views.py:261 users/views.py:998 +#: users/views.py:266 +msgid "The groups were edited." +msgstr "Les groupes ont été modifiés." + +#: users/views.py:285 users/views.py:1031 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: users/views.py:276 +#: users/views.py:300 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: users/views.py:286 +#: users/views.py:310 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: users/views.py:297 +#: users/views.py:321 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: users/views.py:300 users/views.py:354 users/views.py:405 users/views.py:463 -#: users/views.py:551 users/views.py:614 users/views.py:657 +#: users/views.py:324 users/views.py:378 users/views.py:429 users/views.py:487 +#: users/views.py:579 users/views.py:642 users/views.py:685 msgid "Add" msgstr "Ajouter" -#: users/views.py:314 +#: users/views.py:338 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: users/views.py:329 +#: users/views.py:353 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: users/views.py:349 +#: users/views.py:373 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: users/views.py:352 +#: users/views.py:376 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: users/views.py:368 +#: users/views.py:392 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: users/views.py:381 +#: users/views.py:405 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: users/views.py:398 +#: users/views.py:422 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:402 +#: users/views.py:426 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: users/views.py:422 +#: users/views.py:446 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:437 +#: users/views.py:461 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: users/views.py:442 +#: users/views.py:466 msgid "whitelist" msgstr "accès gracieux" -#: users/views.py:457 +#: users/views.py:481 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: users/views.py:480 +#: users/views.py:504 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: users/views.py:503 +#: users/views.py:527 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: users/views.py:508 +#: users/views.py:532 msgid "email address" msgstr "adresse mail" -#: users/views.py:524 +#: users/views.py:548 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:548 +#: users/views.py:551 users/views.py:1075 +msgid "An email to confirm your address was sent." +msgstr "Un mail pour confirmer votre adresse a été envoyé." + +#: users/views.py:576 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: users/views.py:566 +#: users/views.py:594 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: users/views.py:588 +#: users/views.py:616 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: users/views.py:593 +#: users/views.py:621 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1482,31 +1494,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: users/views.py:611 +#: users/views.py:639 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: users/views.py:626 +#: users/views.py:654 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: users/views.py:641 +#: users/views.py:669 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: users/views.py:654 +#: users/views.py:682 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: users/views.py:672 +#: users/views.py:700 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: users/views.py:692 +#: users/views.py:720 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: users/views.py:697 +#: users/views.py:725 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1515,32 +1527,37 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: users/views.py:733 +#: users/views.py:761 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:962 +#: users/views.py:990 users/views.py:1071 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: users/views.py:964 users/views.py:972 +#: users/views.py:992 users/views.py:1000 msgid "Reset" msgstr "Réinitialiser" -#: users/views.py:969 +#: users/views.py:997 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: users/views.py:984 +#: users/views.py:1015 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1020 +#: users/views.py:1051 +#, python-format +msgid "The %s address was confirmed." +msgstr "L'adresse mail %s a été confirmée." + +#: users/views.py:1098 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1032 +#: users/views.py:1110 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1548,15 +1565,7 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: users/views.py:1044 -msgid "The %s address was confirmed." -msgstr "L'adresse mail %s a été confirmée." - -#: users/views.py:1068 -msgid "An email to confirm your address was sent." -msgstr "Un mail pour confirmer votre adresse a été envoyé." - -#: users/views.py:1072 users/views.py:1096 users/views.py:1111 +#: users/views.py:1150 users/views.py:1174 users/views.py:1189 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." diff --git a/users/models.py b/users/models.py index e956a26a..36558a74 100755 --- a/users/models.py +++ b/users/models.py @@ -192,8 +192,8 @@ class User( EMAIL_STATE_UNVERIFIED = 1 EMAIL_STATE_PENDING = 2 EMAIL_STATES = ( - (0, _("Verified")), - (1, _("Unverified")), + (0, _("Confirmed")), + (1, _("Not confirmed")), (2, _("Waiting for email confirmation")), ) From 85836fa77004a35f4fe92bc3dedee564dedc9ddf Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 01:03:41 +0200 Subject: [PATCH 104/490] Cleanup old Request objects in process_passwd and process_email --- users/views.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/users/views.py b/users/views.py index 3d5b0205..28fb107c 100644 --- a/users/views.py +++ b/users/views.py @@ -1027,7 +1027,8 @@ def process_passwd(request, req): u_form.save() reversion.set_comment("Password reset") - req.delete() + # Delete all remaining requests + Request.objects.filter(user=user, type=Request.PASSWD).delete() messages.success(request, _("The password was changed.")) return redirect(reverse("index")) return form( @@ -1047,7 +1048,8 @@ def process_email(request, req): user.save() reversion.set_comment("Email confirmation") - req.delete() + # Delete all remaining requests + Request.objects.filter(user=user, type=Request.EMAIL).delete() messages.success(request, _("The %s address was confirmed." % user.email)) return redirect(reverse("index")) From 283f2729316a1bbabfe0e682fd0a6deb279c4526 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 23:13:36 +0000 Subject: [PATCH 105/490] Add missing migration --- users/migrations/0089_auto_20200418_0112.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 users/migrations/0089_auto_20200418_0112.py diff --git a/users/migrations/0089_auto_20200418_0112.py b/users/migrations/0089_auto_20200418_0112.py new file mode 100644 index 00000000..f3711f0f --- /dev/null +++ b/users/migrations/0089_auto_20200418_0112.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-17 23:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0088_auto_20200417_2312'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email_state', + field=models.IntegerField(choices=[(0, 'Confirmed'), (1, 'Not confirmed'), (2, 'Waiting for email confirmation')], default=2), + ), + ] From 23a5bd05e71cfbf07d6d09736eb860190817adf2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 01:36:24 +0200 Subject: [PATCH 106/490] Add email state statistics --- logs/views.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/logs/views.py b/logs/views.py index 7c509134..5d67f0eb 100644 --- a/logs/views.py +++ b/logs/views.py @@ -284,6 +284,24 @@ def stats_general(request): _all_whitelisted.exclude(adherent__isnull=True).count(), _all_whitelisted.exclude(club__isnull=True).count(), ], + "email_state_verified_users": [ + _("Users with a verified email"), + User.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + ], + "email_state_unverified_users": [ + _("Users with an unverified email"), + User.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + ], + "email_state_pending_users": [ + _("Users pending email confirmation"), + User.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + Adherent.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + Club.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + ], "actives_interfaces": [ _("Active interfaces (with access to the network)"), _all_active_interfaces_count.count(), From 51c81886b5e776508c0672ff6693fbedc767b621 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 17 Apr 2020 23:40:01 +0000 Subject: [PATCH 107/490] Add translations for statistics --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 46 +++++++++++++-------- logs/views.py | 4 +- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 18 ++++---- 13 files changed, 50 insertions(+), 38 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index ccc73c63..2a43ffa3 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 344c4269..bcd86e72 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 6a2bd167..d7c38182 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -159,7 +159,7 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:400 +#: logs/views.py:418 msgid "Actions performed" msgstr "Actions effectuées" @@ -260,65 +260,77 @@ msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" #: logs/views.py:288 +msgid "Users with a confirmed email" +msgstr "Utilisateurs ayant un mail confirmé" + +#: logs/views.py:294 +msgid "Users with an unconfirmed email" +msgstr "Utilisateurs ayant un mail non confirmé" + +#: logs/views.py:300 +msgid "Users pending email confirmation" +msgstr "Utilisateurs en attente de confirmation du mail" + +#: logs/views.py:306 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:302 +#: logs/views.py:320 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:319 +#: logs/views.py:337 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:320 +#: logs/views.py:338 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:321 +#: logs/views.py:339 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:322 +#: logs/views.py:340 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:323 +#: logs/views.py:341 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:324 +#: logs/views.py:342 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:339 +#: logs/views.py:357 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:385 +#: logs/views.py:403 msgid "Topology" msgstr "Topologie" -#: logs/views.py:401 +#: logs/views.py:419 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:426 +#: logs/views.py:444 msgid "rights" msgstr "droits" -#: logs/views.py:455 +#: logs/views.py:473 msgid "actions" msgstr "actions" -#: logs/views.py:486 +#: logs/views.py:504 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:492 +#: logs/views.py:510 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:499 +#: logs/views.py:517 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/logs/views.py b/logs/views.py index 5d67f0eb..78971d18 100644 --- a/logs/views.py +++ b/logs/views.py @@ -285,13 +285,13 @@ def stats_general(request): _all_whitelisted.exclude(club__isnull=True).count(), ], "email_state_verified_users": [ - _("Users with a verified email"), + _("Users with a confirmed email"), User.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), Adherent.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), Club.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), ], "email_state_unverified_users": [ - _("Users with an unverified email"), + _("Users with an unconfirmed email"), User.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), Adherent.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), Club.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index ff54149b..94a700de 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 4457f37a..201e577f 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index d48f427e..4c43a549 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 05954f8f..92760212 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 45226988..ce63a66e 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 6aa9bf63..5fb07ef3 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index b562c90c..977d5325 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 4f4d8217..53df3972 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 6d22504c..bf9d0f26 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 00:48+0200\n" +"POT-Creation-Date: 2020-04-18 01:38+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -1093,7 +1093,7 @@ msgstr "Informations détaillées" msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:165 users/views.py:288 users/views.py:1034 +#: users/templates/users/profil.html:165 users/views.py:288 users/views.py:1035 msgid "Change the password" msgstr "Changer le mot de passe" @@ -1383,7 +1383,7 @@ msgstr "Un mail pour confirmer l'adresse a été envoyé." msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: users/views.py:285 users/views.py:1031 +#: users/views.py:285 users/views.py:1032 msgid "The password was changed." msgstr "Le mot de passe a été changé." @@ -1470,7 +1470,7 @@ msgstr "adresse mail" msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:551 users/views.py:1075 +#: users/views.py:551 users/views.py:1077 msgid "An email to confirm your address was sent." msgstr "Un mail pour confirmer votre adresse a été envoyé." @@ -1532,7 +1532,7 @@ msgstr "" msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:990 users/views.py:1071 +#: users/views.py:990 users/views.py:1073 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." @@ -1548,16 +1548,16 @@ msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1051 +#: users/views.py:1053 #, python-format msgid "The %s address was confirmed." msgstr "L'adresse mail %s a été confirmée." -#: users/views.py:1098 +#: users/views.py:1100 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1110 +#: users/views.py:1112 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1565,7 +1565,7 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: users/views.py:1150 users/views.py:1174 users/views.py:1189 +#: users/views.py:1152 users/views.py:1176 users/views.py:1191 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." From 2f7c77fd9f847664230a6da68c88729e106ee509 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 00:58:48 +0200 Subject: [PATCH 108/490] Fix #247 --- users/models.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/users/models.py b/users/models.py index 36558a74..621617c3 100755 --- a/users/models.py +++ b/users/models.py @@ -1394,14 +1394,15 @@ class Adherent(User): :return: a message and a boolean which is True if the user can create a user or if the `options.all_can_create` is set. """ - if not user_request.is_authenticated and not OptionalUser.get_cached_value( - "self_adhesion" - ): - return False, _("Self registration is disabled."), None + if not user_request.is_authenticated: + if not OptionalUser.get_cached_value( + "self_adhesion" + ): + return False, _("Self registration is disabled."), None + else: + return True, None, None else: - if OptionalUser.get_cached_value( - "all_can_create_adherent" - ) or OptionalUser.get_cached_value("self_adhesion"): + if OptionalUser.get_cached_value("all_can_create_adherent"): return True, None, None else: can = user_request.has_perm("users.add_user") From 022b0525f2ddb3c9a0d077bab15992fc293806bb Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 19:17:04 +0200 Subject: [PATCH 109/490] Fix #248 --- machines/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/machines/models.py b/machines/models.py index 96a6eb48..55fb8838 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1623,15 +1623,15 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): raise ValidationError( _("You can't create a CNAME record pointing to itself.") ) - HOSTNAME_LABEL_PATTERN = re.compile(r"(?!-)[A-Z\d-]+(? 63: + HOSTNAME_LABEL_PATTERN = re.compile(r"(?!-)[a-z\d-]+(? 63: raise ValidationError( - _("The domain name %s is too long (over 63 characters).") % dns + _("The domain name %s is too long (over 63 characters).") % self.name ) - if not HOSTNAME_LABEL_PATTERN.match(dns): + if not HOSTNAME_LABEL_PATTERN.match(self.name): raise ValidationError( - _("The domain name %s contains forbidden characters.") % dns + _("The domain name %s contains forbidden characters.") % self.name ) self.validate_unique() super(Domain, self).clean() From ee9262bb2cbdd268bf70b8a96d83503055909520 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 19:57:14 +0200 Subject: [PATCH 110/490] Fix doublons proprement en migration --- .../migrations/0107_fix_lowercase_domain.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 machines/migrations/0107_fix_lowercase_domain.py diff --git a/machines/migrations/0107_fix_lowercase_domain.py b/machines/migrations/0107_fix_lowercase_domain.py new file mode 100644 index 00000000..c9aebba2 --- /dev/null +++ b/machines/migrations/0107_fix_lowercase_domain.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations +from django.core.exceptions import ValidationError + +def fix_duplicate(apps, schema_editor): + db_alias = schema_editor.connection.alias + domain = apps.get_model("machines", "Domain") + machines_to_fix = list(filter(lambda m : not m.name.islower(), domain.objects.using(db_alias).all())) + for machine in machines_to_fix: + try: + machine.name = machine.name.lower() + machine.validate_unique() + machine.clean() + except ValidationError: + machine.name = machine.name.lower() + str(machine.interface_parent.id) + machine.clean() + machine.save() + +def unfix_duplicate(apps, schema_editor): + return + + +class Migration(migrations.Migration): + + dependencies = [("machines", "0106_auto_20191120_0159")] + + operations = [ + migrations.RunPython(fix_duplicate, unfix_duplicate), + ] From b5c2b4208bee388f99aa15e82d1b6f2ef4744047 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 18 Apr 2020 20:21:43 +0200 Subject: [PATCH 111/490] Add logger message and rename var --- .../migrations/0107_fix_lowercase_domain.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/machines/migrations/0107_fix_lowercase_domain.py b/machines/migrations/0107_fix_lowercase_domain.py index c9aebba2..e47a4680 100644 --- a/machines/migrations/0107_fix_lowercase_domain.py +++ b/machines/migrations/0107_fix_lowercase_domain.py @@ -3,20 +3,25 @@ from __future__ import unicode_literals from django.db import migrations from django.core.exceptions import ValidationError +import logging def fix_duplicate(apps, schema_editor): + logger = logging.getLogger(__name__) db_alias = schema_editor.connection.alias - domain = apps.get_model("machines", "Domain") - machines_to_fix = list(filter(lambda m : not m.name.islower(), domain.objects.using(db_alias).all())) - for machine in machines_to_fix: + Domain = apps.get_model("machines", "Domain") + domains_to_fix = filter(lambda m : not m.name.islower(), Domain.objects.using(db_alias).all()) + for domain in domains_to_fix: try: - machine.name = machine.name.lower() - machine.validate_unique() - machine.clean() + domain.name = domain.name.lower() + domain.validate_unique() + domain.clean() except ValidationError: - machine.name = machine.name.lower() + str(machine.interface_parent.id) - machine.clean() - machine.save() + old_name = domain.name + domain.name = domain.name.lower() + str(domain.interface_parent.id) + domain.clean() + warning_message = "Warning : Domain %s has been renamed %s due to dns uniqueness" % (old_name, domain.name) + logger.warning(warning_message) + domain.save() def unfix_duplicate(apps, schema_editor): return From ca3b3af1c2cc4d2b37c7eead134b4f2453a56526 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 20:09:32 +0000 Subject: [PATCH 112/490] Fix wrong template used for invoices sent by email --- cotisations/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cotisations/utils.py b/cotisations/utils.py index facc1197..95672dd4 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -73,7 +73,8 @@ def send_mail_invoice(invoice): "tpl_path": os.path.join(settings.BASE_DIR, LOGO_PATH), } - pdf = create_pdf("cotisations/factures.tex", ctx) + template = CotisationsOption.get_cached_value("invoice_template").template.name.split("/")[-1] + pdf = create_pdf(template, ctx) template = get_template("cotisations/email_invoice") ctx = { From 36187618ab3fdefa00130f8660c40b94a2870b0e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 18 Apr 2020 20:10:57 +0000 Subject: [PATCH 113/490] Copy default templates during install --- install_re2o.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/install_re2o.sh b/install_re2o.sh index 6cc9a2fb..dcc8d971 100755 --- a/install_re2o.sh +++ b/install_re2o.sh @@ -745,6 +745,7 @@ interactive_guide() { install_webserver "$web_serveur" "$is_tls" "$url_server" + copy_templates_files ########################### From e417de379e3c8b9c6ac999402b39bea7c6ed79ca Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 15 Apr 2020 19:55:35 +0200 Subject: [PATCH 114/490] Add option enabling regular users to take a disabled user's room --- preferences/locale/fr/LC_MESSAGES/django.po | 8 +++++++ ...user_self_force_move_disabled_user_room.py | 20 ++++++++++++++++ preferences/models.py | 3 +++ .../preferences/display_preferences.html | 6 +++-- users/forms.py | 23 +++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 4c43a549..b64fd0a1 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -268,6 +268,10 @@ msgstr "Les utilisateurs peuvent créer un adhérent." msgid "Users can edit their shell." msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande." +#: preferences/models.py:92 +msgid "Users can select a room occupied by a user with a disabled connection." +msgstr "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la connexion est désactivée." + #: preferences/models.py:90 msgid "Users can edit their room." msgstr "Les utilisateurs peuvent modifier leur chambre." @@ -1111,6 +1115,10 @@ msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" msgid "Users can edit their room" msgstr "Les utilisateurs peuvent modifier leur chambre" +#: preferences/templates/preferences/display_preferences.html:144 +msgid "Users can select a room occupied by a user with a disabled connection" +msgstr "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la connexion est désactivée" + #: preferences/templates/preferences/display_preferences.html:154 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" diff --git a/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py b/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py new file mode 100644 index 00000000..97588be6 --- /dev/null +++ b/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-15 16:35 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0067_auto_20191120_0159'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='self_force_move_disabled_user_room', + field=models.BooleanField(default=False, help_text='Users can select a room occupied by a user with a dissabled connection.'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index e470303d..5773bcc3 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -89,6 +89,9 @@ class OptionalUser(AclMixin, PreferencesModel): self_change_room = models.BooleanField( default=False, help_text=_("Users can edit their room.") ) + self_force_move_disabled_user_room = models.BooleanField( + default=False, help_text=_("Users can select a room occupied by a user with a disabled connection.") + ) local_email_accounts_enabled = models.BooleanField( default=False, help_text=_("Enable local email accounts for users.") ) diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 8eb75918..057f7159 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -147,10 +147,12 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Users can edit their room" %} {{ useroptions.self_change_room|tick }} - {% trans "Telephone number required" %} - {{ useroptions.is_tel_mandatory|tick }} + {% trans "Users can select a room occupied by a user with a disabled connection" %} + {{ useroptions.self_force_move_disabled_user_room|tick }} + {% trans "Telephone number required" %} + {{ useroptions.is_tel_mandatory|tick }} {% trans "GPG fingerprint field" %} {{ useroptions.gpg_fingerprint|tick }} diff --git a/users/forms.py b/users/forms.py index 8e60b698..369290aa 100644 --- a/users/forms.py +++ b/users/forms.py @@ -351,6 +351,29 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): label=_("Force the move?"), initial=False, required=False ) + def clean(self): + # Handle case where regular users can force move + can_force_move = OptionalUser.get_cached_value("self_force_move_disabled_user_room") + if not can_force_move: + return super(AdherentForm, self).clean() + + # Ensure the user entered a proper room + room = self.cleaned_data.get("room") + if not room: + return super(AdherentForm, self).clean() + + try: + # If a user already is register for this room + # but their connection has expired, allow force move + user = Adherent.objects.get(room=room) + if user and not user.is_connected(): + remove_user_room(room) + except Adherent.DoesNotExist: + pass + + # Run standard clean process + return super(AdherentForm, self).clean() + def clean_email(self): if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( "email" From c14f172764c4ad917daecdec76260d67a74a5caf Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 15 Apr 2020 18:42:36 +0000 Subject: [PATCH 115/490] Forbid user from forcing out another user with a free access --- .../migrations/0069_auto_20200415_2012.py | 20 +++++++++++++++++++ users/forms.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 preferences/migrations/0069_auto_20200415_2012.py diff --git a/preferences/migrations/0069_auto_20200415_2012.py b/preferences/migrations/0069_auto_20200415_2012.py new file mode 100644 index 00000000..94690d0e --- /dev/null +++ b/preferences/migrations/0069_auto_20200415_2012.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-15 18:12 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0068_optionaluser_self_force_move_disabled_user_room'), + ] + + operations = [ + migrations.AlterField( + model_name='optionaluser', + name='self_force_move_disabled_user_room', + field=models.BooleanField(default=False, help_text='Users can select a room occupied by a user with a disabled connection.'), + ), + ] diff --git a/users/forms.py b/users/forms.py index 369290aa..322c715e 100644 --- a/users/forms.py +++ b/users/forms.py @@ -366,7 +366,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): # If a user already is register for this room # but their connection has expired, allow force move user = Adherent.objects.get(room=room) - if user and not user.is_connected(): + if user and not user.has_access(): remove_user_room(room) except Adherent.DoesNotExist: pass From bf6f3fdec231555fb06b7f681d23e8ebeb0cbcd2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 15 Apr 2020 19:04:14 +0000 Subject: [PATCH 116/490] Make call to remove_user_room cleaner --- re2o/utils.py | 8 +++++--- users/forms.py | 38 +++++++++++++++----------------------- 2 files changed, 20 insertions(+), 26 deletions(-) diff --git a/re2o/utils.py b/re2o/utils.py index 348e5c55..7c49ff0a 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -203,11 +203,13 @@ def all_active_assigned_interfaces_count(): return all_active_interfaces_count().filter(ipv4__isnull=False) -def remove_user_room(room): +def remove_user_room(room, force=True): """ Déménage de force l'ancien locataire de la chambre """ try: user = Adherent.objects.get(room=room) except Adherent.DoesNotExist: return - user.room = None - user.save() + + if force or not user.has_access(): + user.room = None + user.save() diff --git a/users/forms.py b/users/forms.py index 322c715e..a020a427 100644 --- a/users/forms.py +++ b/users/forms.py @@ -351,29 +351,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): label=_("Force the move?"), initial=False, required=False ) - def clean(self): - # Handle case where regular users can force move - can_force_move = OptionalUser.get_cached_value("self_force_move_disabled_user_room") - if not can_force_move: - return super(AdherentForm, self).clean() - - # Ensure the user entered a proper room - room = self.cleaned_data.get("room") - if not room: - return super(AdherentForm, self).clean() - - try: - # If a user already is register for this room - # but their connection has expired, allow force move - user = Adherent.objects.get(room=room) - if user and not user.has_access(): - remove_user_room(room) - except Adherent.DoesNotExist: - pass - - # Run standard clean process - return super(AdherentForm, self).clean() - def clean_email(self): if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( "email" @@ -402,6 +379,21 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): remove_user_room(room) return + def clean_room(self): + """On supprime l'ancien user de la chambre si l'option est activée, + et que l'ancien user a une connexion désactivée""" + # Handle case where regular users can force move + room = self.cleaned_data.get("room") + can_force_move = OptionalUser.get_cached_value("self_force_move_disabled_user_room") + if not can_force_move or not room: + return room + + # Remove the previous user's room, if allowed and necessary + remove_user_room(room, force=False) + + # Run standard clean process + return room + class AdherentCreationForm(AdherentForm): """Formulaire de création d'un user. From 4490f75dd4e847e3a31a64aa9b275139b8a346d7 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 03:16:55 +0200 Subject: [PATCH 117/490] Simplify preferences, add all_room acl for users --- preferences/locale/fr/LC_MESSAGES/django.po | 319 ++++++++++-------- ...user_self_force_move_disabled_user_room.py | 20 -- .../migrations/0069_auto_20200415_2012.py | 20 -- .../migrations/0070_auto_20200419_0225.py | 24 ++ preferences/models.py | 20 +- .../preferences/display_preferences.html | 10 +- users/forms.py | 6 +- users/models.py | 2 +- 8 files changed, 217 insertions(+), 204 deletions(-) delete mode 100644 preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py delete mode 100644 preferences/migrations/0069_auto_20200415_2012.py create mode 100644 preferences/migrations/0070_auto_20200419_0225.py diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index b64fd0a1..d207afba 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 03:09+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,7 +35,7 @@ msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." #: preferences/forms.py:65 -#: preferences/templates/preferences/display_preferences.html:150 +#: preferences/templates/preferences/display_preferences.html:148 msgid "Telephone number required" msgstr "Numéro de téléphone requis" @@ -256,46 +256,64 @@ msgstr "Modèles de document actuels" msgid "Current attributes" msgstr "Attributs actuels" -#: preferences/models.py:77 +#: preferences/models.py:78 +#, fuzzy +#| msgid "Users can edit their room" +msgid "Users can't select their room" +msgstr "Les utilisateurs peuvent modifier leur chambre" + +#: preferences/models.py:79 +#, fuzzy +#| msgid "" +#| "Users can select a room occupied by a user with a disabled connection." +msgid "" +"Users can only select a room occupied by a user with a disabled connection." +msgstr "" +"Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la " +"connexion est désactivée." + +#: preferences/models.py:80 +#, fuzzy +#| msgid "Users can edit their room" +msgid "Users can select all rooms" +msgstr "Les utilisateurs peuvent modifier leur chambre" + +#: preferences/models.py:86 msgid "Users can create a club." msgstr "Les utilisateurs peuvent créer un club." -#: preferences/models.py:80 +#: preferences/models.py:89 msgid "Users can create a member." msgstr "Les utilisateurs peuvent créer un adhérent." -#: preferences/models.py:87 +#: preferences/models.py:95 msgid "Users can edit their shell." msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande." -#: preferences/models.py:92 -msgid "Users can select a room occupied by a user with a disabled connection." -msgstr "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la connexion est désactivée." +#: preferences/models.py:101 +msgid "Policy on self users room edition" +msgstr "Autorisation d'édtion du champ chambre par les utilisateurs" -#: preferences/models.py:90 -msgid "Users can edit their room." -msgstr "Les utilisateurs peuvent modifier leur chambre." - -#: preferences/models.py:93 +#: preferences/models.py:104 msgid "Enable local email accounts for users." msgstr "Activer les comptes mail locaux pour les utilisateurs." -#: preferences/models.py:98 +#: preferences/models.py:109 msgid "Domain to use for local email accounts." msgstr "Domaine à utiliser pour les comptes mail locaux." -#: preferences/models.py:102 +#: preferences/models.py:113 msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:107 +#: preferences/models.py:118 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:113 +#: preferences/models.py:124 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -303,11 +321,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:117 +#: preferences/models.py:128 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:122 +#: preferences/models.py:133 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -315,7 +333,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:129 +#: preferences/models.py:140 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -326,172 +344,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:136 +#: preferences/models.py:147 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:140 +#: preferences/models.py:151 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:141 +#: preferences/models.py:152 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:148 +#: preferences/models.py:159 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:166 +#: preferences/models.py:177 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:167 +#: preferences/models.py:178 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:168 +#: preferences/models.py:179 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:177 +#: preferences/models.py:188 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:187 +#: preferences/models.py:198 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:188 +#: preferences/models.py:199 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:208 preferences/models.py:666 +#: preferences/models.py:219 preferences/models.py:677 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:209 preferences/models.py:667 +#: preferences/models.py:220 preferences/models.py:678 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:215 +#: preferences/models.py:226 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:220 +#: preferences/models.py:231 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:226 +#: preferences/models.py:237 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:233 +#: preferences/models.py:244 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:239 +#: preferences/models.py:250 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:242 +#: preferences/models.py:253 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:245 +#: preferences/models.py:256 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:349 +#: preferences/models.py:360 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:350 +#: preferences/models.py:361 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:363 +#: preferences/models.py:374 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:365 +#: preferences/models.py:376 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:368 +#: preferences/models.py:379 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:372 +#: preferences/models.py:383 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:373 preferences/views.py:331 +#: preferences/models.py:384 preferences/views.py:331 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:374 +#: preferences/models.py:385 #: preferences/templates/preferences/display_preferences.html:207 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:381 +#: preferences/models.py:392 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:384 +#: preferences/models.py:395 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:390 +#: preferences/models.py:401 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:391 +#: preferences/models.py:402 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:393 +#: preferences/models.py:404 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:400 +#: preferences/models.py:411 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:403 preferences/views.py:394 +#: preferences/models.py:414 preferences/views.py:394 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:406 +#: preferences/models.py:417 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:418 +#: preferences/models.py:429 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:424 +#: preferences/models.py:435 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:428 +#: preferences/models.py:439 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:429 preferences/views.py:276 +#: preferences/models.py:440 preferences/views.py:276 msgid "reminder" msgstr "rappel" -#: preferences/models.py:430 +#: preferences/models.py:441 msgid "reminders" msgstr "rappels" -#: preferences/models.py:451 +#: preferences/models.py:462 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -499,7 +517,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:459 +#: preferences/models.py:470 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -507,75 +525,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:474 +#: preferences/models.py:485 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:475 +#: preferences/models.py:486 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:495 +#: preferences/models.py:506 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:496 preferences/views.py:227 +#: preferences/models.py:507 preferences/views.py:227 msgid "service" msgstr "service" -#: preferences/models.py:497 +#: preferences/models.py:508 msgid "services" msgstr "services" -#: preferences/models.py:507 +#: preferences/models.py:518 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:513 +#: preferences/models.py:524 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:523 +#: preferences/models.py:534 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:525 +#: preferences/models.py:536 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:526 +#: preferences/models.py:537 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:534 preferences/views.py:634 +#: preferences/models.py:545 preferences/views.py:634 msgid "mandate" msgstr "mandat" -#: preferences/models.py:535 +#: preferences/models.py:546 msgid "mandates" msgstr "mandats" -#: preferences/models.py:536 +#: preferences/models.py:547 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:543 +#: preferences/models.py:554 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:544 +#: preferences/models.py:555 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:546 +#: preferences/models.py:557 msgid "start date" msgstr "date de début" -#: preferences/models.py:547 +#: preferences/models.py:558 msgid "end date" msgstr "date de fin" -#: preferences/models.py:560 +#: preferences/models.py:571 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -583,140 +601,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:575 +#: preferences/models.py:586 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:578 +#: preferences/models.py:589 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:579 +#: preferences/models.py:590 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:582 +#: preferences/models.py:593 msgid "Organisation" msgstr "Association" -#: preferences/models.py:589 +#: preferences/models.py:600 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:590 +#: preferences/models.py:601 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:608 +#: preferences/models.py:619 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:609 +#: preferences/models.py:620 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:623 +#: preferences/models.py:634 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:626 +#: preferences/models.py:637 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:631 +#: preferences/models.py:642 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:633 +#: preferences/models.py:644 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:638 +#: preferences/models.py:649 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:639 +#: preferences/models.py:650 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:643 preferences/views.py:587 +#: preferences/models.py:654 preferences/views.py:587 msgid "attribute" msgstr "attribut" -#: preferences/models.py:644 +#: preferences/models.py:655 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:646 +#: preferences/models.py:657 msgid "value" msgstr "valeur" -#: preferences/models.py:648 +#: preferences/models.py:659 msgid "comment" msgstr "commentaire" -#: preferences/models.py:649 +#: preferences/models.py:660 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:660 +#: preferences/models.py:671 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:661 +#: preferences/models.py:672 #: preferences/templates/preferences/display_preferences.html:285 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:672 +#: preferences/models.py:683 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:673 +#: preferences/models.py:684 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:682 +#: preferences/models.py:693 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:690 +#: preferences/models.py:701 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:691 +#: preferences/models.py:702 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:697 +#: preferences/models.py:708 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:698 +#: preferences/models.py:709 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:704 +#: preferences/models.py:715 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:712 +#: preferences/models.py:723 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:713 +#: preferences/models.py:724 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:719 +#: preferences/models.py:730 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:720 +#: preferences/models.py:731 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:727 +#: preferences/models.py:738 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -724,87 +742,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:737 +#: preferences/models.py:748 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:738 +#: preferences/models.py:749 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:744 +#: preferences/models.py:755 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:745 +#: preferences/models.py:756 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:751 +#: preferences/models.py:762 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:759 +#: preferences/models.py:770 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:760 +#: preferences/models.py:771 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:766 +#: preferences/models.py:777 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:767 +#: preferences/models.py:778 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:773 +#: preferences/models.py:784 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:781 +#: preferences/models.py:792 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:782 +#: preferences/models.py:793 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:788 +#: preferences/models.py:799 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:789 +#: preferences/models.py:800 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:802 +#: preferences/models.py:813 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:803 +#: preferences/models.py:814 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:830 +#: preferences/models.py:841 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:834 +#: preferences/models.py:845 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:841 +#: preferences/models.py:852 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:847 +#: preferences/models.py:858 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:849 +#: preferences/models.py:860 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -812,19 +830,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:861 +#: preferences/models.py:872 msgid "template" msgstr "modèle" -#: preferences/models.py:862 +#: preferences/models.py:873 msgid "name" msgstr "nom" -#: preferences/models.py:865 +#: preferences/models.py:876 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:866 +#: preferences/models.py:877 msgid "document templates" msgstr "modèles de document" @@ -1111,18 +1129,14 @@ msgstr "Interface en ligne de commande par défaut pour les utilisateurs" msgid "Users can edit their shell" msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" -#: preferences/templates/preferences/display_preferences.html:148 -msgid "Users can edit their room" -msgstr "Les utilisateurs peuvent modifier leur chambre" - -#: preferences/templates/preferences/display_preferences.html:144 -msgid "Users can select a room occupied by a user with a disabled connection" -msgstr "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la connexion est désactivée" - -#: preferences/templates/preferences/display_preferences.html:154 +#: preferences/templates/preferences/display_preferences.html:150 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" +#: preferences/templates/preferences/display_preferences.html:154 +msgid "Policy for self-user room change" +msgstr "Autorisations pour le changement de chambre des utilisateurs" + #: preferences/templates/preferences/display_preferences.html:165 msgid "Machine preferences" msgstr "Préférences de machine" @@ -1485,3 +1499,12 @@ msgstr "Le mandat a été modifié." #: preferences/views.py:631 msgid "The mandate was deleted." msgstr "Le mandat été supprimé." + +#~ msgid "Users can edit their room." +#~ msgstr "Les utilisateurs peuvent modifier leur chambre." + +#~ msgid "" +#~ "Users can select a room occupied by a user with a disabled connection" +#~ msgstr "" +#~ "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la " +#~ "connexion est désactivée" diff --git a/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py b/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py deleted file mode 100644 index 97588be6..00000000 --- a/preferences/migrations/0068_optionaluser_self_force_move_disabled_user_room.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.28 on 2020-04-15 16:35 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0067_auto_20191120_0159'), - ] - - operations = [ - migrations.AddField( - model_name='optionaluser', - name='self_force_move_disabled_user_room', - field=models.BooleanField(default=False, help_text='Users can select a room occupied by a user with a dissabled connection.'), - ), - ] diff --git a/preferences/migrations/0069_auto_20200415_2012.py b/preferences/migrations/0069_auto_20200415_2012.py deleted file mode 100644 index 94690d0e..00000000 --- a/preferences/migrations/0069_auto_20200415_2012.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.28 on 2020-04-15 18:12 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('preferences', '0068_optionaluser_self_force_move_disabled_user_room'), - ] - - operations = [ - migrations.AlterField( - model_name='optionaluser', - name='self_force_move_disabled_user_room', - field=models.BooleanField(default=False, help_text='Users can select a room occupied by a user with a disabled connection.'), - ), - ] diff --git a/preferences/migrations/0070_auto_20200419_0225.py b/preferences/migrations/0070_auto_20200419_0225.py new file mode 100644 index 00000000..6dd70fa4 --- /dev/null +++ b/preferences/migrations/0070_auto_20200419_0225.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-19 00:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0069_optionaluser_disable_emailnotyetconfirmed'), + ] + + operations = [ + migrations.RemoveField( + model_name='optionaluser', + name='self_change_room', + ), + migrations.AddField( + model_name='optionaluser', + name='self_room_policy', + field=models.CharField(choices=[('DISABLED', "Users can't select their room"), ('ONLY_INACTIVE', 'Users can only select a room occupied by a user with a disabled connection.'), ('ALL_ROOM', 'Users can select all rooms')], default='DISABLED', help_text='Policy on self users room edition', max_length=32), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 5773bcc3..66a72f46 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -71,6 +71,15 @@ class OptionalUser(AclMixin, PreferencesModel): """Options pour l'user : obligation ou nom du telephone, activation ou non du solde, autorisation du negatif, fingerprint etc""" + DISABLED = "DISABLED" + ONLY_INACTIVE = "ONLY_INACTIVE" + ALL_ROOM = "ALL_ROOM" + ROOM_POLICY = ( + (DISABLED, _("Users can't select their room")), + (ONLY_INACTIVE, _("Users can only select a room occupied by a user with a disabled connection.")), + (ALL_ROOM, _("Users can select all rooms")), + ) + is_tel_mandatory = models.BooleanField(default=True) gpg_fingerprint = models.BooleanField(default=True) all_can_create_club = models.BooleanField( @@ -79,18 +88,17 @@ class OptionalUser(AclMixin, PreferencesModel): all_can_create_adherent = models.BooleanField( default=False, help_text=_("Users can create a member.") ) - shell_default = models.OneToOneField( "users.ListShell", on_delete=models.PROTECT, blank=True, null=True ) self_change_shell = models.BooleanField( default=False, help_text=_("Users can edit their shell.") ) - self_change_room = models.BooleanField( - default=False, help_text=_("Users can edit their room.") - ) - self_force_move_disabled_user_room = models.BooleanField( - default=False, help_text=_("Users can select a room occupied by a user with a disabled connection.") + self_room_policy = models.CharField( + max_length=32, + choices=ROOM_POLICY, + default="DISABLED", + help_text=_("Policy on self users room edition") ) local_email_accounts_enabled = models.BooleanField( default=False, help_text=_("Enable local email accounts for users.") diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 057f7159..2142eb68 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -144,18 +144,16 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Users can edit their shell" %} {{ useroptions.self_change_shell|tick }} - - {% trans "Users can edit their room" %} - {{ useroptions.self_change_room|tick }} - {% trans "Users can select a room occupied by a user with a disabled connection" %} - {{ useroptions.self_force_move_disabled_user_room|tick }} - {% trans "Telephone number required" %} {{ useroptions.is_tel_mandatory|tick }} {% trans "GPG fingerprint field" %} {{ useroptions.gpg_fingerprint|tick }} + + {% trans "Policy for self-user room change" %} + {{ useroptions.self_room_policy }} +
diff --git a/users/forms.py b/users/forms.py index a020a427..e4f39f10 100644 --- a/users/forms.py +++ b/users/forms.py @@ -384,12 +384,12 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): et que l'ancien user a une connexion désactivée""" # Handle case where regular users can force move room = self.cleaned_data.get("room") - can_force_move = OptionalUser.get_cached_value("self_force_move_disabled_user_room") - if not can_force_move or not room: + room_policy = OptionalUser.get_cached_value("self_room_policy") + if room_policy == OptionalUser.DISABLED or not room: return room # Remove the previous user's room, if allowed and necessary - remove_user_room(room, force=False) + remove_user_room(room, force=bool(room_policy == OptionalUser.ALL_ROOM)) # Run standard clean process return room diff --git a/users/models.py b/users/models.py index 621617c3..6b260170 100755 --- a/users/models.py +++ b/users/models.py @@ -1110,7 +1110,7 @@ class User( if not ( ( self.pk == user_request.pk - and OptionalUser.get_cached_value("self_change_room") + and OptionalUser.get_cached_value("self_room_policy") != OptionalUser.DISABLED ) or user_request.has_perm("users.change_user") ): From 0c1eaa956d521aa235b09a27bee76c578edfce12 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 15:15:27 +0200 Subject: [PATCH 118/490] Fix trads --- preferences/locale/fr/LC_MESSAGES/django.po | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index d207afba..37c85224 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -257,15 +257,10 @@ msgid "Current attributes" msgstr "Attributs actuels" #: preferences/models.py:78 -#, fuzzy -#| msgid "Users can edit their room" msgid "Users can't select their room" -msgstr "Les utilisateurs peuvent modifier leur chambre" +msgstr "Les utilisateurs ne peuvent pas modifier leur chambre" #: preferences/models.py:79 -#, fuzzy -#| msgid "" -#| "Users can select a room occupied by a user with a disabled connection." msgid "" "Users can only select a room occupied by a user with a disabled connection." msgstr "" @@ -273,10 +268,8 @@ msgstr "" "connexion est désactivée." #: preferences/models.py:80 -#, fuzzy -#| msgid "Users can edit their room" msgid "Users can select all rooms" -msgstr "Les utilisateurs peuvent modifier leur chambre" +msgstr "Les utilisateurs peuvent choisir toutes les chambres" #: preferences/models.py:86 msgid "Users can create a club." From f8a55bbbe599da1724375203f251e7b2cc7263aa Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 16:42:17 +0200 Subject: [PATCH 119/490] Fix #240 --- .../migrations/0074_auto_20200419_1640.py | 19 +++++++++++++++++++ topologie/models.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 topologie/migrations/0074_auto_20200419_1640.py diff --git a/topologie/migrations/0074_auto_20200419_1640.py b/topologie/migrations/0074_auto_20200419_1640.py new file mode 100644 index 00000000..22576704 --- /dev/null +++ b/topologie/migrations/0074_auto_20200419_1640.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-19 14:40 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('topologie', '0073_auto_20191120_0159'), + ] + + operations = [ + migrations.AlterModelOptions( + name='portprofile', + options={'permissions': (('view_portprofile', 'Can view a port profile object'),), 'verbose_name': 'port profile', 'verbose_name_plural': 'port profiles'}, + ), + ] diff --git a/topologie/models.py b/topologie/models.py index ab6a22dd..34c390bb 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -893,7 +893,7 @@ class PortProfile(AclMixin, RevMixin, models.Model): ) class Meta: - permissions = (("view_port_profile", _("Can view a port profile object")),) + permissions = (("view_portprofile", _("Can view a port profile object")),) verbose_name = _("port profile") verbose_name_plural = _("port profiles") unique_together = ["on_dormitory", "profil_default"] From 47740c4a91c08d5a2c747084fe2f4405700d7913 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 17:07:29 +0200 Subject: [PATCH 120/490] Add email state search filter --- search/engine.py | 20 +++++++++++++++----- search/forms.py | 13 +++++++++++++ search/views.py | 4 +++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/search/engine.py b/search/engine.py index 3dfd0680..e0ed1018 100644 --- a/search/engine.py +++ b/search/engine.py @@ -166,7 +166,8 @@ def contains_filter(attribute, word, case_sensitive=False): def search_single_word(word, filters, user, start, end, - user_state, aff, case_sensitive=False): + user_state, email_state, aff, + case_sensitive=False): """ 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 @@ -199,6 +200,9 @@ def search_single_word(word, filters, user, start, end, filter_clubs &= Q(state__in=user_state) filter_users &= Q(state__in=user_state) + filter_clubs &= Q(email_state__in=email_state) + filter_users &= Q(email_state__in=email_state) + filters["users"] |= filter_users filters["clubs"] |= filter_clubs @@ -207,7 +211,8 @@ def search_single_word(word, filters, user, start, end, filter_machines = ( contains_filter("name", word, case_sensitive) | (contains_filter("user__pseudo", word, case_sensitive) - & Q(user__state__in=user_state)) + & Q(user__state__in=user_state) + & Q(user__email_state__in=email_state)) | contains_filter("interface__domain__name", word, case_sensitive) | contains_filter("interface__domain__related_domain__name", word, case_sensitive) @@ -228,6 +233,7 @@ def search_single_word(word, filters, user, start, end, filter_factures = ( contains_filter("user__pseudo", word, case_sensitive) & Q(user__state__in=user_state) + & Q(user__email_state__in=email_state) ) if start is not None: filter_factures &= Q(date__gte=start) @@ -240,6 +246,7 @@ def search_single_word(word, filters, user, start, end, filter_bans = ( contains_filter("user__pseudo", word, case_sensitive) & Q(user__state__in=user_state) + & Q(user__email_state__in=email_state) ) | contains_filter("raison", word, case_sensitive) if start is not None: filter_bans &= ( @@ -260,6 +267,7 @@ def search_single_word(word, filters, user, start, end, filter_whitelists = ( contains_filter("user__pseudo", word, case_sensitive) & Q(user__state__in=user_state) + & Q(user__email_state__in=email_state) ) | contains_filter("raison", word, case_sensitive) if start is not None: filter_whitelists &= ( @@ -397,7 +405,7 @@ def apply_filters(filters, user, aff): return results -def search_single_query(query, filters, user, start, end, user_state, aff): +def search_single_query(query, filters, user, start, end, user_state, email_state, aff): """ Handle different queries an construct the correct filters using search_single_word""" if query.operator == "+": @@ -406,7 +414,8 @@ def search_single_query(query, filters, user, start, end, user_state, aff): for q in query.subqueries: # Construct an independent filter for each subquery subfilters = search_single_query(q, empty_filters(), user, - start, end, user_state, aff) + start, end, user_state, + email_state, aff) # Apply the subfilter for field in filter_fields(): @@ -420,7 +429,8 @@ def search_single_query(query, filters, user, start, end, user_state, aff): # Handle standard queries return search_single_word(query.text, filters, user, start, end, - user_state, aff, query.case_sensitive) + user_state, email_state, aff, + query.case_sensitive) def create_queries(query): diff --git a/search/forms.py b/search/forms.py index 9f2ff82a..2c495b5a 100644 --- a/search/forms.py +++ b/search/forms.py @@ -37,6 +37,12 @@ CHOICES_USER = ( ("4", _("Fully archived")), ) +CHOICES_EMAILS = ( + ("0", _("Confirmed")), + ("1", _("Not confirmed")), + ("2", _("Waiting for email confirmation")), +) + CHOICES_AFF = ( ("0", _("Users")), ("1", _("Machines")), @@ -93,6 +99,13 @@ class SearchFormPlus(Form): choices=CHOICES_USER, initial=initial_choices(CHOICES_USER), ) + m = forms.MultipleChoiceField( + label=_("Email state filter"), + required=False, + widget=forms.CheckboxSelectMultiple, + choices=CHOICES_EMAILS, + initial=initial_choices(CHOICES_EMAILS), + ) a = forms.MultipleChoiceField( label=_("Display filter"), required=False, diff --git a/search/views.py b/search/views.py index a994240f..00141ed1 100644 --- a/search/views.py +++ b/search/views.py @@ -38,6 +38,7 @@ from search.forms import ( SearchForm, SearchFormPlus, CHOICES_USER, + CHOICES_EMAILS, CHOICES_AFF, initial_choices, ) @@ -56,6 +57,7 @@ def get_results(query, request, params): start = params.get("s", None) end = params.get("e", None) user_state = params.get("u", initial_choices(CHOICES_USER)) + email_state = params.get("m", initial_choices(CHOICES_EMAILS)) aff = params.get("a", initial_choices(CHOICES_AFF)) filters = empty_filters() @@ -63,7 +65,7 @@ def get_results(query, request, params): queries = create_queries(query) for q in queries: filters = search_single_query( - q, filters, request.user, start, end, user_state, aff + q, filters, request.user, start, end, user_state, email_state, aff ) results = apply_filters(filters, request.user, aff) From 2ac9edaed88b0b3d330af6581d454bc28a240342 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 15:11:57 +0000 Subject: [PATCH 121/490] Show email state field in advanced search --- search/templates/search/search.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/search/templates/search/search.html b/search/templates/search/search.html index 5f54da97..d1a9d5f8 100644 --- a/search/templates/search/search.html +++ b/search/templates/search/search.html @@ -36,6 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if search_form.u %} {% include 'buttons/multiple_checkbox_alt.html' with field=search_form.u %} {% endif %} + {% if search_form.m %} + {% include 'buttons/multiple_checkbox_alt.html' with field=search_form.m %} + {% endif %} {% if search_form.a %} {% include 'buttons/multiple_checkbox_alt.html' with field=search_form.a %} {% endif %} From 449e1f872146a47ad601bdeec9dbd8eeede9f9df Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 15:15:40 +0000 Subject: [PATCH 122/490] Add translations for advanced search email filter --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 2 +- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 49 ++++-- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 182 ++++++++++---------- 12 files changed, 132 insertions(+), 119 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 2a43ffa3..8d2012f7 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index bcd86e72..bc915638 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index d7c38182..aceb2172 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 94a700de..8bd28bf3 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 201e577f..9a3def0b 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 37c85224..f3ca1867 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 03:09+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 92760212..e2db4702 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index ce63a66e..1420cc69 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -51,43 +51,55 @@ msgid "Fully archived" msgstr "Complètement archivés" #: search/forms.py:41 +msgid "Confirmed" +msgstr "Confirmé" + +#: search/forms.py:42 +msgid "Not confirmed" +msgstr "Non confirmé" + +#: search/forms.py:43 +msgid "Waiting for email confirmation" +msgstr "En attente de confirmation du mail" + +#: search/forms.py:47 msgid "Users" msgstr "Utilisateurs" -#: search/forms.py:42 +#: search/forms.py:48 msgid "Machines" msgstr "Machines" -#: search/forms.py:43 +#: search/forms.py:49 msgid "Invoices" msgstr "Factures" -#: search/forms.py:44 +#: search/forms.py:50 msgid "Bans" msgstr "Bannissements" -#: search/forms.py:45 +#: search/forms.py:51 msgid "Whitelists" msgstr "Accès gracieux" -#: search/forms.py:46 +#: search/forms.py:52 msgid "Rooms" msgstr "Chambres" -#: search/forms.py:47 +#: search/forms.py:53 msgid "Ports" msgstr "Ports" -#: search/forms.py:48 +#: search/forms.py:54 msgid "Switches" msgstr "Commutateurs réseau" -#: search/forms.py:62 search/forms.py:78 search/templates/search/search.html:29 -#: search/templates/search/search.html:48 +#: search/forms.py:68 search/forms.py:84 search/templates/search/search.html:29 +#: search/templates/search/search.html:51 msgid "Search" msgstr "Rechercher" -#: search/forms.py:65 search/forms.py:81 +#: search/forms.py:71 search/forms.py:87 msgid "" "Use « » and «,» to specify distinct words, «\"query\"» for an exact search, " "«\\» to escape a character and «+» to combine keywords." @@ -96,19 +108,23 @@ msgstr "" "recherche exacte, «\\» pour échapper un caractère et «+» pour combiner des " "mots clés." -#: search/forms.py:90 +#: search/forms.py:96 msgid "Users filter" msgstr "Filtre utilisateurs" -#: search/forms.py:97 +#: search/forms.py:103 +msgid "Email state filter" +msgstr "Filtre mails" + +#: search/forms.py:110 msgid "Display filter" msgstr "Filtre affichage" -#: search/forms.py:103 +#: search/forms.py:116 msgid "Start date" msgstr "Date de début" -#: search/forms.py:104 +#: search/forms.py:117 msgid "End date" msgstr "Date de fin" @@ -176,6 +192,3 @@ msgstr "Recherche avancée" #~ msgid "Unverified" #~ msgstr "Non-confirmé" - -#~ msgid "Waiting for email confirmation" -#~ msgstr "En attente de confirmation du mail" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 5fb07ef3..1d00e8e8 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 977d5325..2cb96239 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 53df3972..76a26f28 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index bf9d0f26..5b91a471 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-18 01:38+0200\n" +"POT-Creation-Date: 2020-04-19 17:12+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -39,7 +39,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Current password" msgstr "Mot de passe actuel" -#: users/forms.py:83 users/forms.py:613 users/forms.py:641 +#: users/forms.py:83 users/forms.py:628 users/forms.py:656 msgid "New password" msgstr "Nouveau mot de passe" @@ -55,12 +55,12 @@ msgstr "Les nouveaux mots de passe ne correspondent pas." msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: users/forms.py:132 users/forms.py:189 users/forms.py:410 -#: users/models.py:1893 +#: users/forms.py:132 users/forms.py:189 users/forms.py:425 +#: users/models.py:1894 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:138 users/forms.py:192 users/forms.py:417 +#: users/forms.py:138 users/forms.py:192 users/forms.py:432 msgid "Password confirmation" msgstr "Confirmation du mot de passe" @@ -73,7 +73,7 @@ msgid "You can't use an internal address as your external address." msgstr "" "Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe." -#: users/forms.py:169 users/forms.py:212 users/forms.py:491 +#: users/forms.py:169 users/forms.py:212 users/forms.py:506 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." @@ -114,36 +114,36 @@ msgstr "Prénom" msgid "Surname" msgstr "Nom" -#: users/forms.py:329 users/forms.py:551 users/models.py:1893 +#: users/forms.py:329 users/forms.py:566 users/models.py:1894 #: users/templates/users/aff_emailaddress.html:36 #: users/templates/users/profil.html:209 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:330 users/forms.py:549 users/forms.py:694 +#: users/forms.py:330 users/forms.py:564 users/forms.py:709 #: users/templates/users/aff_schools.html:37 #: users/templates/users/profil.html:230 msgid "School" msgstr "Établissement" -#: users/forms.py:331 users/forms.py:550 +#: users/forms.py:331 users/forms.py:565 #: users/templates/users/aff_serviceusers.html:34 #: users/templates/users/profil.html:235 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:333 users/forms.py:553 +#: users/forms.py:333 users/forms.py:568 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 #: users/templates/users/profil.html:214 msgid "Room" msgstr "Chambre" -#: users/forms.py:334 users/forms.py:554 +#: users/forms.py:334 users/forms.py:569 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:335 users/forms.py:555 +#: users/forms.py:335 users/forms.py:570 msgid "Select a school" msgstr "Sélectionnez un établissement" @@ -151,15 +151,15 @@ msgstr "Sélectionnez un établissement" msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:361 users/forms.py:845 +#: users/forms.py:361 users/forms.py:860 msgid "You can't use a {} address." msgstr "Vous ne pouvez pas utiliser une adresse {}." -#: users/forms.py:371 users/forms.py:578 +#: users/forms.py:371 users/forms.py:593 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:390 +#: users/forms.py:405 msgid "" "If this options is set, you will receive a link to set your initial password " "by email. If you do not have any means of accessing your emails, you can " @@ -174,11 +174,11 @@ msgstr "" "votre adresse dans les délais impartis, votre connexion sera automatiquement " "suspendue." -#: users/forms.py:404 +#: users/forms.py:419 msgid "Send password reset link by email." msgstr "Envoyer le lien de modification du mot de passe par mail." -#: users/forms.py:425 +#: users/forms.py:440 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -189,101 +189,101 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:432 +#: users/forms.py:447 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:457 +#: users/forms.py:472 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:459 +#: users/forms.py:474 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:477 +#: users/forms.py:492 msgid "Password must contain at least 8 characters." msgstr "Le mot de passe doit contenir au moins 8 caractères." -#: users/forms.py:518 +#: users/forms.py:533 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: users/forms.py:521 +#: users/forms.py:536 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: users/forms.py:548 users/templates/users/aff_clubs.html:36 +#: users/forms.py:563 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:556 +#: users/forms.py:571 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:662 users/templates/users/profil.html:277 +#: users/forms.py:677 users/templates/users/profil.html:277 msgid "State" msgstr "État" -#: users/forms.py:663 +#: users/forms.py:678 msgid "Email state" msgstr "État du mail" -#: users/forms.py:681 users/templates/users/aff_listright.html:38 +#: users/forms.py:696 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:707 +#: users/forms.py:722 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:727 +#: users/forms.py:742 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:739 +#: users/forms.py:754 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:748 +#: users/forms.py:763 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:766 +#: users/forms.py:781 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:785 users/forms.py:800 users/templates/users/aff_bans.html:41 +#: users/forms.py:800 users/forms.py:815 users/templates/users/aff_bans.html:41 #: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: users/forms.py:815 +#: users/forms.py:830 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:816 +#: users/forms.py:831 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:832 +#: users/forms.py:847 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:834 +#: users/forms.py:849 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:836 +#: users/forms.py:851 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:888 +#: users/forms.py:903 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:893 +#: users/forms.py:908 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" @@ -336,7 +336,7 @@ msgstr "Non confirmé" msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:204 users/models.py:1549 +#: users/models.py:204 users/models.py:1550 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." @@ -487,7 +487,7 @@ msgstr "Vous n'avez pas le droit de voir ce club." msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1283 users/models.py:1487 +#: users/models.py:1283 users/models.py:1488 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." @@ -527,183 +527,183 @@ msgstr "adhérents" msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1400 +#: users/models.py:1401 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1410 +#: users/models.py:1411 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1440 +#: users/models.py:1441 msgid "club" msgstr "club" -#: users/models.py:1441 +#: users/models.py:1442 msgid "clubs" msgstr "clubs" -#: users/models.py:1452 +#: users/models.py:1453 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1460 +#: users/models.py:1461 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1553 +#: users/models.py:1554 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1559 +#: users/models.py:1560 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1560 users/views.py:356 +#: users/models.py:1561 users/views.py:356 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1561 +#: users/models.py:1562 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1565 +#: users/models.py:1566 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1632 +#: users/models.py:1633 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1633 +#: users/models.py:1634 msgid "school" msgstr "établissement" -#: users/models.py:1634 +#: users/models.py:1635 msgid "schools" msgstr "établissements" -#: users/models.py:1653 +#: users/models.py:1654 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1659 +#: users/models.py:1660 msgid "Description." msgstr "Description." -#: users/models.py:1662 +#: users/models.py:1663 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1663 +#: users/models.py:1664 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1664 +#: users/models.py:1665 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1709 +#: users/models.py:1710 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1710 users/views.py:671 +#: users/models.py:1711 users/views.py:671 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1711 +#: users/models.py:1712 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1729 +#: users/models.py:1730 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1730 +#: users/models.py:1731 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1731 +#: users/models.py:1732 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1741 +#: users/models.py:1742 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1742 users/views.py:407 +#: users/models.py:1743 users/views.py:407 msgid "ban" msgstr "bannissement" -#: users/models.py:1743 +#: users/models.py:1744 msgid "bans" msgstr "bannissements" -#: users/models.py:1778 +#: users/models.py:1779 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1826 +#: users/models.py:1827 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1827 +#: users/models.py:1828 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1828 +#: users/models.py:1829 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1848 +#: users/models.py:1849 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2046 +#: users/models.py:2047 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2049 +#: users/models.py:2050 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2054 +#: users/models.py:2055 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2056 +#: users/models.py:2057 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2057 +#: users/models.py:2058 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2085 users/models.py:2120 users/models.py:2154 -#: users/models.py:2188 +#: users/models.py:2086 users/models.py:2121 users/models.py:2155 +#: users/models.py:2189 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2090 +#: users/models.py:2091 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2100 +#: users/models.py:2101 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2126 +#: users/models.py:2127 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2146 +#: users/models.py:2147 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -711,13 +711,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2160 +#: users/models.py:2161 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2180 +#: users/models.py:2181 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -725,13 +725,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2194 +#: users/models.py:2195 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2203 +#: users/models.py:2204 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." From cb9ae34418ba60a95f46a5fa6db92f62a5d4aca7 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 20:06:34 +0200 Subject: [PATCH 123/490] Make emails throw timeout errors, and gracefully handle them --- cotisations/models.py | 11 ++++++++--- cotisations/utils.py | 27 +++++++++++++++++++++++---- cotisations/views.py | 2 +- re2o/settings.py | 3 +++ re2o/utils.py | 18 ++++++++++++++++++ tickets/models.py | 9 ++++++--- tickets/views.py | 2 ++ users/models.py | 32 +++++++++++++++++++++++--------- users/views.py | 8 ++++++++ 9 files changed, 92 insertions(+), 20 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index 215fcdb5..503ad244 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -330,9 +330,14 @@ class Facture(BaseInvoice): def save(self, *args, **kwargs): super(Facture, self).save(*args, **kwargs) + + request = None + if "request" in kwargs: + request = kwargs["request"] + if not self.__original_valid and self.valid: self.reorder_purchases() - send_mail_invoice(self) + send_mail_invoice(self, request) if ( self.is_subscription() and not self.__original_control @@ -340,7 +345,7 @@ class Facture(BaseInvoice): and CotisationsOption.get_cached_value("send_voucher_mail") and self.user.is_adherent() ): - send_mail_voucher(self) + send_mail_voucher(self, request) def __str__(self): return str(self.user) + " " + str(self.date) @@ -870,7 +875,7 @@ class Paiement(RevMixin, AclMixin, models.Model): # So make this invoice valid, trigger send mail invoice.valid = True - invoice.save() + invoice.save(request) # In case a cotisation was bought, inform the user, the # cotisation time has been extended too diff --git a/cotisations/utils.py b/cotisations/utils.py index 95672dd4..c370fe59 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -23,6 +23,9 @@ import os from django.template.loader import get_template from django.core.mail import EmailMessage +from django.utils.translation import ugettext_lazy as _ +from django.contrib import messages +from smtplib import SMTPException from .tex import create_pdf from preferences.models import AssoOption, GeneralOption, CotisationsOption, Mandate @@ -43,7 +46,21 @@ def find_payment_method(payment): return None -def send_mail_invoice(invoice): +def send_mail(mail, request): + """Wrapper for Django's EmailMessage.send which handles errors""" + try: + mail.send() + except SMTPException as e: + if request: + messages.error( + request, + _("Failed to send email: %(error)s.") % { + "error": e, + }, + ) + + +def send_mail_invoice(invoice, request=None): """Creates the pdf of the invoice and sends it by email to the client""" purchases_info = [] for purchase in invoice.vente_set.all(): @@ -90,10 +107,11 @@ def send_mail_invoice(invoice): [invoice.user.get_mail], attachments=[("invoice.pdf", pdf, "application/pdf")], ) - mail.send() + + send_mail(mail, request) -def send_mail_voucher(invoice): +def send_mail_voucher(invoice, request=None): """Creates a voucher from an invoice and sends it by email to the client""" president = Mandate.get_mandate(invoice.date).president ctx = { @@ -126,4 +144,5 @@ def send_mail_voucher(invoice): [invoice.user.get_mail], attachments=[("voucher.pdf", pdf, "application/pdf")], ) - mail.send() + + send_mail(mail, request) diff --git a/cotisations/views.py b/cotisations/views.py index cc80e22d..def26cda 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -1008,7 +1008,7 @@ def credit_solde(request, user, **_kwargs): else: price_ok = True if price_ok: - invoice.save() + invoice.save(request) Vente.objects.create( facture=invoice, name="solde", diff --git a/re2o/settings.py b/re2o/settings.py index 3d883615..c6df1a69 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -181,6 +181,9 @@ MEDIA_URL = os.path.join(BASE_DIR, "/media/") # Models to use for graphs GRAPH_MODELS = {"all_applications": True, "group_models": True} +# Timeout when sending emails through Django (in seconds) +EMAIL_TIMEOUT = 10 + # Activate API if "api" in INSTALLED_APPS: from api.settings import * diff --git a/re2o/utils.py b/re2o/utils.py index 7c49ff0a..0e735720 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -39,6 +39,10 @@ from __future__ import unicode_literals from django.utils import timezone from django.db.models import Q from django.contrib.auth.models import Permission +from django.utils.translation import ugettext_lazy as _ +from django.core.mail import send_mail as django_send_mail +from django.contrib import messages +from smtplib import SMTPException from cotisations.models import Cotisation, Facture, Vente from machines.models import Interface, Machine @@ -213,3 +217,17 @@ def remove_user_room(room, force=True): if force or not user.has_access(): user.room = None user.save() + + +def send_mail(request, *args, **kwargs): + """Wrapper for Django's send_mail which handles errors""" + try: + kwargs["fail_silently"] = request is None + django_send_mail(*args, **kwargs) + except SMTPException as e: + messages.error( + request, + _("Failed to send email: %(error)s.") % { + "error": e, + }, + ) diff --git a/tickets/models.py b/tickets/models.py index 55827097..5b74248c 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -1,11 +1,11 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ -from django.core.mail import send_mail from django.template import loader from django.db.models.signals import post_save from django.dispatch import receiver from re2o.mixins import AclMixin +from re2o.utils import send_mail from preferences.models import GeneralOption @@ -38,6 +38,7 @@ class Ticket(AclMixin, models.Model): help_text=_("An email address to get back to you."), max_length=100, null=True ) solved = models.BooleanField(default=False) + request = None class Meta: permissions = (("view_tickets", _("Can view a ticket object")),) @@ -50,7 +51,7 @@ class Ticket(AclMixin, models.Model): else: return _("Anonymous ticket. Date: %s.") % (self.date) - def publish_mail(self): + def publish_mail(self, request=None): site_url = GeneralOption.objects.first().main_site_url to_addr = Preferences.objects.first().publish_address context = {"ticket": self, "site_url": site_url} @@ -62,7 +63,9 @@ class Ticket(AclMixin, models.Model): else: obj = "New ticket opened" template = loader.get_template("tickets/publication_mail_en") + send_mail( + request, obj, template.render(context), GeneralOption.get_cached_value("email_from"), @@ -108,4 +111,4 @@ def ticket_post_save(**kwargs): if kwargs["created"]: if Preferences.objects.first().publish_address: ticket = kwargs["instance"] - ticket.publish_mail() + ticket.publish_mail(ticket.request) diff --git a/tickets/views.py b/tickets/views.py index d330719a..03cc535c 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -60,6 +60,8 @@ def new_ticket(request): if ticketform.is_valid(): email = ticketform.cleaned_data.get("email") ticket = ticketform.save(commit=False) + ticket.request = request + if request.user.is_authenticated: ticket.user = request.user ticket.save() diff --git a/users/models.py b/users/models.py index 6b260170..55acd051 100755 --- a/users/models.py +++ b/users/models.py @@ -60,7 +60,6 @@ from django.db.models.signals import post_save, post_delete, m2m_changed from django.dispatch import receiver from django.utils.functional import cached_property from django.template import loader -from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db import transaction from django.utils import timezone @@ -84,6 +83,7 @@ from re2o.settings import LDAP, GID_RANGES, UID_RANGES from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin from re2o.base import smtp_check +from re2o.utils import send_mail from cotisations.models import Cotisation, Facture, Paiement, Vente from machines.models import Domain, Interface, Machine, regen @@ -244,6 +244,7 @@ class User( REQUIRED_FIELDS = ["surname", "email"] objects = UserManager() + request = None class Meta: permissions = ( @@ -749,7 +750,7 @@ class User( name__in=list(queryset_users.values_list("pseudo", flat=True)) ) - def notif_inscription(self): + def notif_inscription(self, request=None): """ Prend en argument un objet user, envoie un mail de bienvenue """ template = loader.get_template("users/email_welcome") mailmessageoptions, _created = MailMessageOption.objects.get_or_create() @@ -761,7 +762,9 @@ class User( "welcome_mail_en": mailmessageoptions.welcome_mail_en, "pseudo": self.pseudo, } + send_mail( + request, "Bienvenue au %(name)s / Welcome to %(name)s" % {"name": AssoOption.get_cached_value("name")}, "", @@ -769,7 +772,6 @@ class User( [self.email], html_message=template.render(context), ) - return def reset_passwd_mail(self, request): """ Prend en argument un request, envoie un mail de @@ -789,7 +791,9 @@ class User( ), "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), } + send_mail( + request, "Changement de mot de passe de %(name)s / Password change for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, template.render(context), @@ -797,7 +801,6 @@ class User( [req.user.email], fail_silently=False, ) - return def send_confirm_email_if_necessary(self, request): """Update the user's email state: @@ -873,7 +876,9 @@ class User( "confirm_before_fr": self.confirm_email_before_date().strftime("%d/%m/%Y"), "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), } + send_mail( + request, "Confirmation du mail de %(name)s / Email confirmation for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, template.render(context), @@ -883,7 +888,7 @@ class User( ) return - def autoregister_machine(self, mac_address, nas_type): + def autoregister_machine(self, mac_address, nas_type, request=None): """ Fonction appellée par freeradius. Enregistre la mac pour une machine inconnue sur le compte de l'user""" allowed, _message, _rights = Machine.can_create(self, self.id) @@ -927,7 +932,9 @@ class User( "asso_email": AssoOption.get_cached_value("contact"), "pseudo": self.pseudo, } + send_mail( + None, "Ajout automatique d'une machine / New machine autoregistered", "", GeneralOption.get_cached_value("email_from"), @@ -936,7 +943,7 @@ class User( ) return - def notif_disable(self): + def notif_disable(self, request=None): """Envoi un mail de notification informant que l'adresse mail n'a pas été confirmée""" template = loader.get_template("users/email_disable_notif") context = { @@ -945,7 +952,9 @@ class User( "asso_email": AssoOption.get_cached_value("contact"), "site_name": GeneralOption.get_cached_value("site_name"), } + send_mail( + request, "Suspension automatique / Automatic suspension", template.render(context), GeneralOption.get_cached_value("email_from"), @@ -1509,8 +1518,10 @@ def user_post_save(**kwargs): is_created = kwargs["created"] user = kwargs["instance"] EMailAddress.objects.get_or_create(local_part=user.pseudo.lower(), user=user) + if is_created: - user.notif_inscription() + user.notif_inscription(user.request) + user.state_sync() user.ldap_sync( base=True, access_refresh=True, mac_refresh=False, group_refresh=True @@ -1737,13 +1748,14 @@ class Ban(RevMixin, AclMixin, models.Model): date_start = models.DateTimeField(auto_now_add=True) date_end = models.DateTimeField() state = models.IntegerField(choices=STATES, default=STATE_HARD) + request = None class Meta: permissions = (("view_ban", _("Can view a ban object")),) verbose_name = _("ban") verbose_name_plural = _("bans") - def notif_ban(self): + def notif_ban(self, request=None): """ Prend en argument un objet ban, envoie un mail de notification """ template = loader.get_template("users/email_ban_notif") context = { @@ -1752,7 +1764,9 @@ class Ban(RevMixin, AclMixin, models.Model): "date_end": self.date_end, "asso_name": AssoOption.get_cached_value("name"), } + send_mail( + request, "Déconnexion disciplinaire / Disciplinary disconnection", template.render(context), GeneralOption.get_cached_value("email_from"), @@ -1795,7 +1809,7 @@ def ban_post_save(**kwargs): user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) regen("mailing") if is_created: - ban.notif_ban() + ban.notif_ban(ban.request) regen("dhcp") regen("mac_ip_list") if user.has_access(): diff --git a/users/views.py b/users/views.py index 28fb107c..1c544ba6 100644 --- a/users/views.py +++ b/users/views.py @@ -119,6 +119,8 @@ def new_user(request): """ Vue de création d'un nouvel utilisateur, envoie un mail pour le mot de passe""" user = AdherentCreationForm(request.POST or None, user=request.user) + user.request = request + GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") GTU = GeneralOption.get_cached_value("GTU") is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") @@ -165,6 +167,8 @@ def new_club(request): """ Vue de création d'un nouveau club, envoie un mail pour le mot de passe""" club = ClubForm(request.POST or None, user=request.user) + club.request = request + if club.is_valid(): club = club.save(commit=False) club.save() @@ -368,6 +372,8 @@ def add_ban(request, user, userid): Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" ban_instance = Ban(user=user) ban = BanForm(request.POST or None, instance=ban_instance) + ban.request = request + if ban.is_valid(): ban.save() messages.success(request, _("The ban was added.")) @@ -386,6 +392,8 @@ def edit_ban(request, ban_instance, **_kwargs): (a fortiori bureau) Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" ban = BanForm(request.POST or None, instance=ban_instance) + ban.request = request + if ban.is_valid(): if ban.changed_data: ban.save() From 91504e968878c48d6e40d0704f41f90b4437f1aa Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 20:11:38 +0200 Subject: [PATCH 124/490] Fix circular import of utils --- tickets/models.py | 4 ++-- users/models.py | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tickets/models.py b/tickets/models.py index 5b74248c..17736978 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -5,7 +5,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from re2o.mixins import AclMixin -from re2o.utils import send_mail +import re2o.utils from preferences.models import GeneralOption @@ -64,7 +64,7 @@ class Ticket(AclMixin, models.Model): obj = "New ticket opened" template = loader.get_template("tickets/publication_mail_en") - send_mail( + re2o.utils.send_mail( request, obj, template.render(context), diff --git a/users/models.py b/users/models.py index 55acd051..9fb6a4b8 100755 --- a/users/models.py +++ b/users/models.py @@ -83,7 +83,7 @@ from re2o.settings import LDAP, GID_RANGES, UID_RANGES from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin from re2o.base import smtp_check -from re2o.utils import send_mail +import re2o.utils from cotisations.models import Cotisation, Facture, Paiement, Vente from machines.models import Domain, Interface, Machine, regen @@ -763,7 +763,7 @@ class User( "pseudo": self.pseudo, } - send_mail( + re2o.utils.send_mail( request, "Bienvenue au %(name)s / Welcome to %(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -792,7 +792,7 @@ class User( "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), } - send_mail( + re2o.utils.send_mail( request, "Changement de mot de passe de %(name)s / Password change for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -877,7 +877,7 @@ class User( "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), } - send_mail( + re2o.utils.send_mail( request, "Confirmation du mail de %(name)s / Email confirmation for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -933,7 +933,7 @@ class User( "pseudo": self.pseudo, } - send_mail( + re2o.utils.send_mail( None, "Ajout automatique d'une machine / New machine autoregistered", "", @@ -953,7 +953,7 @@ class User( "site_name": GeneralOption.get_cached_value("site_name"), } - send_mail( + re2o.utils.send_mail( request, "Suspension automatique / Automatic suspension", template.render(context), @@ -1765,7 +1765,7 @@ class Ban(RevMixin, AclMixin, models.Model): "asso_name": AssoOption.get_cached_value("name"), } - send_mail( + re2o.utils.send_mail( request, "Déconnexion disciplinaire / Disciplinary disconnection", template.render(context), From 00c15b4bb9267542f1a62a0c4ce770d9fba4647e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 20:15:23 +0200 Subject: [PATCH 125/490] Move mail util function to seperate file --- re2o/mail_utils.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ re2o/utils.py | 18 ------------------ tickets/models.py | 4 ++-- users/models.py | 14 +++++++------- 4 files changed, 54 insertions(+), 27 deletions(-) create mode 100644 re2o/mail_utils.py diff --git a/re2o/mail_utils.py b/re2o/mail_utils.py new file mode 100644 index 00000000..065a9506 --- /dev/null +++ b/re2o/mail_utils.py @@ -0,0 +1,45 @@ +# -*- 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 © 2020 Jean-Romain Garnier +# +# 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. + +# -*- coding: utf-8 -*- +# Jean-Romain Garnier +""" +Regroupe les fonctions en lien avec les mails +""" + +from django.utils.translation import ugettext_lazy as _ +from django.core.mail import send_mail as django_send_mail +from django.contrib import messages +from smtplib import SMTPException + + +def send_mail(request, *args, **kwargs): + """Wrapper for Django's send_mail which handles errors""" + try: + kwargs["fail_silently"] = request is None + django_send_mail(*args, **kwargs) + except SMTPException as e: + messages.error( + request, + _("Failed to send email: %(error)s.") % { + "error": e, + }, + ) diff --git a/re2o/utils.py b/re2o/utils.py index 0e735720..7c49ff0a 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -39,10 +39,6 @@ from __future__ import unicode_literals from django.utils import timezone from django.db.models import Q from django.contrib.auth.models import Permission -from django.utils.translation import ugettext_lazy as _ -from django.core.mail import send_mail as django_send_mail -from django.contrib import messages -from smtplib import SMTPException from cotisations.models import Cotisation, Facture, Vente from machines.models import Interface, Machine @@ -217,17 +213,3 @@ def remove_user_room(room, force=True): if force or not user.has_access(): user.room = None user.save() - - -def send_mail(request, *args, **kwargs): - """Wrapper for Django's send_mail which handles errors""" - try: - kwargs["fail_silently"] = request is None - django_send_mail(*args, **kwargs) - except SMTPException as e: - messages.error( - request, - _("Failed to send email: %(error)s.") % { - "error": e, - }, - ) diff --git a/tickets/models.py b/tickets/models.py index 17736978..a8adbe87 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -5,7 +5,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from re2o.mixins import AclMixin -import re2o.utils +from re2o.mail_utils import send_mail from preferences.models import GeneralOption @@ -64,7 +64,7 @@ class Ticket(AclMixin, models.Model): obj = "New ticket opened" template = loader.get_template("tickets/publication_mail_en") - re2o.utils.send_mail( + send_mail( request, obj, template.render(context), diff --git a/users/models.py b/users/models.py index 9fb6a4b8..42df3f5a 100755 --- a/users/models.py +++ b/users/models.py @@ -83,7 +83,7 @@ from re2o.settings import LDAP, GID_RANGES, UID_RANGES from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin from re2o.base import smtp_check -import re2o.utils +from re2o.mail_utils import send_mail from cotisations.models import Cotisation, Facture, Paiement, Vente from machines.models import Domain, Interface, Machine, regen @@ -763,7 +763,7 @@ class User( "pseudo": self.pseudo, } - re2o.utils.send_mail( + send_mail( request, "Bienvenue au %(name)s / Welcome to %(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -792,7 +792,7 @@ class User( "expire_in": str(GeneralOption.get_cached_value("req_expire_hrs")), } - re2o.utils.send_mail( + send_mail( request, "Changement de mot de passe de %(name)s / Password change for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -877,7 +877,7 @@ class User( "confirm_before_en": self.confirm_email_before_date().strftime("%Y-%m-%d"), } - re2o.utils.send_mail( + send_mail( request, "Confirmation du mail de %(name)s / Email confirmation for " "%(name)s" % {"name": AssoOption.get_cached_value("name")}, @@ -933,7 +933,7 @@ class User( "pseudo": self.pseudo, } - re2o.utils.send_mail( + send_mail( None, "Ajout automatique d'une machine / New machine autoregistered", "", @@ -953,7 +953,7 @@ class User( "site_name": GeneralOption.get_cached_value("site_name"), } - re2o.utils.send_mail( + send_mail( request, "Suspension automatique / Automatic suspension", template.render(context), @@ -1765,7 +1765,7 @@ class Ban(RevMixin, AclMixin, models.Model): "asso_name": AssoOption.get_cached_value("name"), } - re2o.utils.send_mail( + send_mail( request, "Déconnexion disciplinaire / Disciplinary disconnection", template.render(context), From fa95625205ff34d5dde80b71725549acaca8dc6c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 18:21:38 +0000 Subject: [PATCH 126/490] Add translation for email error message --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 149 +++++----- logs/locale/fr/LC_MESSAGES/django.po | 2 +- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 7 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 26 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 300 ++++++++++---------- 12 files changed, 254 insertions(+), 244 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 8d2012f7..fb747f3e 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index bc915638..ec0098c2 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" @@ -37,7 +37,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:682 +#: cotisations/forms.py:73 cotisations/models.py:687 msgid "Member" msgstr "Adhérent" @@ -154,7 +154,7 @@ msgstr "Peut voir un objet facture" msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:145 cotisations/models.py:456 cotisations/views.py:376 +#: cotisations/models.py:145 cotisations/models.py:461 cotisations/views.py:376 #: cotisations/views.py:571 msgid "invoice" msgstr "facture" @@ -217,115 +217,115 @@ msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:372 +#: cotisations/models.py:377 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:374 +#: cotisations/models.py:379 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:375 +#: cotisations/models.py:380 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:376 +#: cotisations/models.py:381 msgid "address" msgstr "adresse" -#: cotisations/models.py:377 +#: cotisations/models.py:382 msgid "paid" msgstr "payé" -#: cotisations/models.py:378 +#: cotisations/models.py:383 msgid "remark" msgstr "remarque" -#: cotisations/models.py:383 +#: cotisations/models.py:388 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:386 +#: cotisations/models.py:391 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:421 +#: cotisations/models.py:426 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:427 +#: cotisations/models.py:432 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:449 cotisations/models.py:688 -#: cotisations/models.py:946 +#: cotisations/models.py:454 cotisations/models.py:693 +#: cotisations/models.py:951 msgid "Connection" msgstr "Connexion" -#: cotisations/models.py:450 cotisations/models.py:689 -#: cotisations/models.py:947 +#: cotisations/models.py:455 cotisations/models.py:694 +#: cotisations/models.py:952 msgid "Membership" msgstr "Adhésion" -#: cotisations/models.py:451 cotisations/models.py:684 -#: cotisations/models.py:690 cotisations/models.py:948 +#: cotisations/models.py:456 cotisations/models.py:689 +#: cotisations/models.py:695 cotisations/models.py:953 msgid "Both of them" msgstr "Les deux" -#: cotisations/models.py:460 +#: cotisations/models.py:465 msgid "amount" msgstr "montant" -#: cotisations/models.py:463 +#: cotisations/models.py:468 msgid "article" msgstr "article" -#: cotisations/models.py:466 +#: cotisations/models.py:471 msgid "price" msgstr "prix" -#: cotisations/models.py:469 cotisations/models.py:702 +#: cotisations/models.py:474 cotisations/models.py:707 msgid "duration (in months)" msgstr "durée (en mois)" -#: cotisations/models.py:475 cotisations/models.py:708 +#: cotisations/models.py:480 cotisations/models.py:713 msgid "duration (in days, will be added to duration in months)" msgstr "durée (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:483 cotisations/models.py:722 -#: cotisations/models.py:959 +#: cotisations/models.py:488 cotisations/models.py:727 +#: cotisations/models.py:964 msgid "subscription type" msgstr "type de cotisation" -#: cotisations/models.py:488 +#: cotisations/models.py:493 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:489 +#: cotisations/models.py:494 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:491 cotisations/models.py:953 +#: cotisations/models.py:496 cotisations/models.py:958 msgid "purchase" msgstr "achat" -#: cotisations/models.py:492 +#: cotisations/models.py:497 msgid "purchases" msgstr "achats" -#: cotisations/models.py:545 cotisations/models.py:742 +#: cotisations/models.py:550 cotisations/models.py:747 msgid "Duration must be specified for a subscription." msgstr "La durée doit être renseignée pour une cotisation." -#: cotisations/models.py:556 +#: cotisations/models.py:561 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:562 +#: cotisations/models.py:567 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:571 +#: cotisations/models.py:576 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -333,15 +333,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:586 +#: cotisations/models.py:591 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:592 +#: cotisations/models.py:597 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:599 +#: cotisations/models.py:604 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -349,134 +349,134 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:615 +#: cotisations/models.py:620 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:683 +#: cotisations/models.py:688 msgid "Club" msgstr "Club" -#: cotisations/models.py:693 +#: cotisations/models.py:698 msgid "designation" msgstr "désignation" -#: cotisations/models.py:696 +#: cotisations/models.py:701 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:714 +#: cotisations/models.py:719 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:725 cotisations/models.py:826 +#: cotisations/models.py:730 cotisations/models.py:831 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:732 +#: cotisations/models.py:737 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:733 +#: cotisations/models.py:738 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:740 +#: cotisations/models.py:745 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:765 +#: cotisations/models.py:770 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:806 +#: cotisations/models.py:811 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:807 +#: cotisations/models.py:812 msgid "bank" msgstr "banque" -#: cotisations/models.py:808 +#: cotisations/models.py:813 msgid "banks" msgstr "banques" -#: cotisations/models.py:824 +#: cotisations/models.py:829 msgid "method" msgstr "moyen" -#: cotisations/models.py:831 +#: cotisations/models.py:836 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:832 +#: cotisations/models.py:837 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:838 +#: cotisations/models.py:843 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:839 +#: cotisations/models.py:844 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:841 +#: cotisations/models.py:846 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:842 +#: cotisations/models.py:847 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:881 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:886 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:891 +#: cotisations/models.py:896 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:911 +#: cotisations/models.py:916 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:930 +#: cotisations/models.py:935 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:961 +#: cotisations/models.py:966 msgid "start date" msgstr "date de début" -#: cotisations/models.py:962 +#: cotisations/models.py:967 msgid "end date" msgstr "date de fin" -#: cotisations/models.py:966 +#: cotisations/models.py:971 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:967 +#: cotisations/models.py:972 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:969 +#: cotisations/models.py:974 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:970 +#: cotisations/models.py:975 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:976 +#: cotisations/models.py:981 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:985 +#: cotisations/models.py:990 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -484,11 +484,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:997 +#: cotisations/models.py:1002 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:1004 +#: cotisations/models.py:1009 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -496,7 +496,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1020 +#: cotisations/models.py:1025 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -933,6 +933,11 @@ msgstr "Créer une facture" msgid "Control the invoices" msgstr "Contrôler les factures" +#: cotisations/utils.py:57 +#, python-format +msgid "Failed to send email: %(error)s." +msgstr "" + #: cotisations/views.py:157 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index aceb2172..22de19e5 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 8bd28bf3..5ec18685 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 9a3def0b..31245d63 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index f3ca1867..bf0387b0 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index e2db4702..1c408790 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -74,6 +74,11 @@ msgstr "Format : {main} {more}" msgid "Format: {main}" msgstr "Format : {main}" +#: re2o/mail_utils.py:42 +#, python-format +msgid "Failed to send email: %(error)s." +msgstr "Échec de l'envoi du mail : %(error)s." + #: re2o/mixins.py:113 #, python-format msgid "You don't have the right to create a %s object." diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 1420cc69..bf54fb7d 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 1d00e8e8..3935e92a 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 2cb96239..166576ba 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -42,33 +42,33 @@ msgstr "Description du ticket." msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:43 +#: tickets/models.py:44 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:44 +#: tickets/models.py:45 msgid "ticket" msgstr "ticket" -#: tickets/models.py:45 +#: tickets/models.py:46 msgid "tickets" msgstr "tickets" -#: tickets/models.py:49 +#: tickets/models.py:50 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:51 +#: tickets/models.py:52 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:82 +#: tickets/models.py:85 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:94 +#: tickets/models.py:97 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." @@ -214,7 +214,7 @@ msgstr "Modifier" msgid "Ticket opening" msgstr "Ouverture de ticket" -#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:88 +#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:90 msgid "" "You are not authenticated. Please log in or provide an email address so we " "can get back to you." @@ -280,21 +280,21 @@ msgstr "Langue du mail" msgid "No tickets" msgstr "Pas de tickets" -#: tickets/views.py:69 tickets/views.py:80 +#: tickets/views.py:71 tickets/views.py:82 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:125 tickets/views.py:175 +#: tickets/views.py:127 tickets/views.py:177 msgid "Never" msgstr "Jamais" -#: tickets/views.py:152 +#: tickets/views.py:154 msgid "The tickets preferences were edited." msgstr "Les préférences de tickets ont été modifiées." -#: tickets/views.py:155 +#: tickets/views.py:157 msgid "Invalid form." msgstr "Formulaire invalide." diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 76a26f28..37a56a9d 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 5b91a471..35774bd3 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 17:12+0200\n" +"POT-Creation-Date: 2020-04-19 20:16+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -56,7 +56,7 @@ msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." #: users/forms.py:132 users/forms.py:189 users/forms.py:425 -#: users/models.py:1894 +#: users/models.py:1908 msgid "Password" msgstr "Mot de passe" @@ -114,7 +114,7 @@ msgstr "Prénom" msgid "Surname" msgstr "Nom" -#: users/forms.py:329 users/forms.py:566 users/models.py:1894 +#: users/forms.py:329 users/forms.py:566 users/models.py:1908 #: users/templates/users/aff_emailaddress.html:36 #: users/templates/users/profil.html:209 msgid "Email address" @@ -336,7 +336,7 @@ msgstr "Non confirmé" msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:204 users/models.py:1550 +#: users/models.py:204 users/models.py:1561 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." @@ -362,82 +362,82 @@ msgstr "Commentaire, promotion." msgid "enable shortcuts on Re2o website" msgstr "activer les raccourcis sur le site de Re2o" -#: users/models.py:250 +#: users/models.py:251 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: users/models.py:251 +#: users/models.py:252 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: users/models.py:252 +#: users/models.py:253 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: users/models.py:253 +#: users/models.py:254 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:256 +#: users/models.py:257 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:258 +#: users/models.py:259 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:259 +#: users/models.py:260 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:261 +#: users/models.py:262 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:262 +#: users/models.py:263 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:280 users/models.py:308 users/models.py:318 +#: users/models.py:281 users/models.py:309 users/models.py:319 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:314 users/templates/users/aff_listright.html:75 +#: users/models.py:315 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:316 +#: users/models.py:317 msgid "Club" msgstr "Club" -#: users/models.py:891 +#: users/models.py:896 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:893 +#: users/models.py:898 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:916 users/templates/users/user_autocapture.html:64 +#: users/models.py:921 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: users/models.py:1010 +#: users/models.py:1019 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:1024 users/models.py:1078 +#: users/models.py:1033 users/models.py:1087 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:1036 +#: users/models.py:1045 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:1043 +#: users/models.py:1052 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -445,61 +445,61 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:1055 users/models.py:1093 +#: users/models.py:1064 users/models.py:1102 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:1119 +#: users/models.py:1128 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1136 +#: users/models.py:1145 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1156 +#: users/models.py:1165 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1173 users/models.py:1188 +#: users/models.py:1182 users/models.py:1197 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1203 +#: users/models.py:1212 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1218 +#: users/models.py:1227 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1234 +#: users/models.py:1243 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1259 +#: users/models.py:1268 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1268 +#: users/models.py:1277 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1283 users/models.py:1488 +#: users/models.py:1292 users/models.py:1497 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1300 +#: users/models.py:1309 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1323 +#: users/models.py:1332 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1331 +#: users/models.py:1340 msgid "" "There is neither a local email address nor an external email address for " "this user." @@ -507,7 +507,7 @@ msgstr "" "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " "utilisateur." -#: users/models.py:1338 +#: users/models.py:1347 msgid "" "You can't redirect your local emails if no external email address has been " "set." @@ -515,195 +515,195 @@ msgstr "" "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail externe " "n'a été définie." -#: users/models.py:1358 +#: users/models.py:1367 msgid "member" msgstr "adhérent" -#: users/models.py:1359 +#: users/models.py:1368 msgid "members" msgstr "adhérents" -#: users/models.py:1376 +#: users/models.py:1385 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1401 +#: users/models.py:1410 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1411 +#: users/models.py:1420 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1441 +#: users/models.py:1450 msgid "club" msgstr "club" -#: users/models.py:1442 +#: users/models.py:1451 msgid "clubs" msgstr "clubs" -#: users/models.py:1453 +#: users/models.py:1462 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1461 +#: users/models.py:1470 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1554 +#: users/models.py:1565 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1560 +#: users/models.py:1571 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1561 users/views.py:356 +#: users/models.py:1572 users/views.py:360 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1562 +#: users/models.py:1573 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1566 +#: users/models.py:1577 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1633 +#: users/models.py:1644 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1634 +#: users/models.py:1645 msgid "school" msgstr "établissement" -#: users/models.py:1635 +#: users/models.py:1646 msgid "schools" msgstr "établissements" -#: users/models.py:1654 +#: users/models.py:1665 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1660 +#: users/models.py:1671 msgid "Description." msgstr "Description." -#: users/models.py:1663 +#: users/models.py:1674 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1664 +#: users/models.py:1675 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1665 +#: users/models.py:1676 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1710 +#: users/models.py:1721 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1711 users/views.py:671 +#: users/models.py:1722 users/views.py:679 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1712 +#: users/models.py:1723 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1730 +#: users/models.py:1741 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1731 +#: users/models.py:1742 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1732 +#: users/models.py:1743 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1742 +#: users/models.py:1754 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1743 users/views.py:407 +#: users/models.py:1755 users/views.py:415 msgid "ban" msgstr "bannissement" -#: users/models.py:1744 +#: users/models.py:1756 msgid "bans" msgstr "bannissements" -#: users/models.py:1779 +#: users/models.py:1793 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1827 +#: users/models.py:1841 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1828 +#: users/models.py:1842 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1829 +#: users/models.py:1843 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1849 +#: users/models.py:1863 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2047 +#: users/models.py:2061 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2050 +#: users/models.py:2064 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2055 +#: users/models.py:2069 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2057 +#: users/models.py:2071 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2058 +#: users/models.py:2072 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2086 users/models.py:2121 users/models.py:2155 -#: users/models.py:2189 +#: users/models.py:2100 users/models.py:2135 users/models.py:2169 +#: users/models.py:2203 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2091 +#: users/models.py:2105 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2101 +#: users/models.py:2115 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2127 +#: users/models.py:2141 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2147 +#: users/models.py:2161 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -711,13 +711,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2161 +#: users/models.py:2175 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2181 +#: users/models.py:2195 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -725,13 +725,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2195 +#: users/models.py:2209 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2204 +#: users/models.py:2218 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." @@ -888,8 +888,8 @@ msgstr "pour %(name)s." #: users/templates/users/confirm_email.html:39 #: users/templates/users/delete.html:36 #: users/templates/users/mass_archive.html:36 -#: users/templates/users/resend_confirmation_email.html:35 users/views.py:628 -#: users/views.py:732 +#: users/templates/users/resend_confirmation_email.html:35 users/views.py:636 +#: users/views.py:740 msgid "Confirm" msgstr "Confirmer" @@ -1086,14 +1086,14 @@ msgstr "Pas de machine" msgid "Detailed information" msgstr "Informations détaillées" -#: users/templates/users/profil.html:161 users/views.py:202 users/views.py:233 -#: users/views.py:252 users/views.py:269 users/views.py:341 users/views.py:395 -#: users/views.py:449 users/views.py:514 users/views.py:561 users/views.py:597 -#: users/views.py:657 users/views.py:703 +#: users/templates/users/profil.html:161 users/views.py:206 users/views.py:237 +#: users/views.py:256 users/views.py:273 users/views.py:345 users/views.py:403 +#: users/views.py:457 users/views.py:522 users/views.py:569 users/views.py:605 +#: users/views.py:665 users/views.py:711 msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:165 users/views.py:288 users/views.py:1035 +#: users/templates/users/profil.html:165 users/views.py:292 users/views.py:1043 msgid "Change the password" msgstr "Changer le mot de passe" @@ -1333,160 +1333,160 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: users/views.py:133 +#: users/views.py:135 #, python-format msgid "The user %s was created, a confirmation email was sent." msgstr "L'utilisateur %s a été créé, un mail de confirmation a été envoyé." -#: users/views.py:140 +#: users/views.py:142 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: users/views.py:153 +#: users/views.py:155 msgid "Commit" msgstr "Valider" -#: users/views.py:174 +#: users/views.py:178 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: users/views.py:179 +#: users/views.py:183 msgid "Create a club" msgstr "Créer un club" -#: users/views.py:194 +#: users/views.py:198 msgid "The club was edited." msgstr "Le club a été modifié." -#: users/views.py:226 +#: users/views.py:230 msgid "The user was edited." msgstr "L'utilisateur a été modifié." -#: users/views.py:229 +#: users/views.py:233 msgid "Sent a new confirmation email." msgstr "Un nouveau mail de confirmation a été envoyé." -#: users/views.py:247 +#: users/views.py:251 msgid "The states were edited." msgstr "Les états ont été modifié." -#: users/views.py:249 +#: users/views.py:253 msgid "An email to confirm the address was sent." msgstr "Un mail pour confirmer l'adresse a été envoyé." -#: users/views.py:266 +#: users/views.py:270 msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: users/views.py:285 users/views.py:1032 +#: users/views.py:289 users/views.py:1040 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: users/views.py:300 +#: users/views.py:304 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: users/views.py:310 +#: users/views.py:314 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: users/views.py:321 +#: users/views.py:325 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: users/views.py:324 users/views.py:378 users/views.py:429 users/views.py:487 -#: users/views.py:579 users/views.py:642 users/views.py:685 +#: users/views.py:328 users/views.py:384 users/views.py:437 users/views.py:495 +#: users/views.py:587 users/views.py:650 users/views.py:693 msgid "Add" msgstr "Ajouter" -#: users/views.py:338 +#: users/views.py:342 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: users/views.py:353 +#: users/views.py:357 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: users/views.py:373 +#: users/views.py:379 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: users/views.py:376 +#: users/views.py:382 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: users/views.py:392 +#: users/views.py:400 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: users/views.py:405 +#: users/views.py:413 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: users/views.py:422 +#: users/views.py:430 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:426 +#: users/views.py:434 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: users/views.py:446 +#: users/views.py:454 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:461 +#: users/views.py:469 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: users/views.py:466 +#: users/views.py:474 msgid "whitelist" msgstr "accès gracieux" -#: users/views.py:481 +#: users/views.py:489 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: users/views.py:504 +#: users/views.py:512 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: users/views.py:527 +#: users/views.py:535 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: users/views.py:532 +#: users/views.py:540 msgid "email address" msgstr "adresse mail" -#: users/views.py:548 +#: users/views.py:556 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:551 users/views.py:1077 +#: users/views.py:559 users/views.py:1085 msgid "An email to confirm your address was sent." msgstr "Un mail pour confirmer votre adresse a été envoyé." -#: users/views.py:576 +#: users/views.py:584 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: users/views.py:594 +#: users/views.py:602 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: users/views.py:616 +#: users/views.py:624 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: users/views.py:621 +#: users/views.py:629 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1494,31 +1494,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: users/views.py:639 +#: users/views.py:647 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: users/views.py:654 +#: users/views.py:662 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: users/views.py:669 +#: users/views.py:677 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: users/views.py:682 +#: users/views.py:690 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: users/views.py:700 +#: users/views.py:708 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: users/views.py:720 +#: users/views.py:728 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: users/views.py:725 +#: users/views.py:733 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1527,37 +1527,37 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: users/views.py:761 +#: users/views.py:769 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:990 users/views.py:1073 +#: users/views.py:998 users/views.py:1081 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: users/views.py:992 users/views.py:1000 +#: users/views.py:1000 users/views.py:1008 msgid "Reset" msgstr "Réinitialiser" -#: users/views.py:997 +#: users/views.py:1005 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: users/views.py:1015 +#: users/views.py:1023 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1053 +#: users/views.py:1061 #, python-format msgid "The %s address was confirmed." msgstr "L'adresse mail %s a été confirmée." -#: users/views.py:1100 +#: users/views.py:1108 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1112 +#: users/views.py:1120 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1565,7 +1565,7 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: users/views.py:1152 users/views.py:1176 users/views.py:1191 +#: users/views.py:1160 users/views.py:1184 users/views.py:1199 msgid "The mailing list doesn't exist." msgstr "La liste de diffusion n'existe pas." From cd097cd428dd3406384a6ba8da09ebd4e97b8ce2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 21:13:31 +0200 Subject: [PATCH 127/490] Also catch ConnectionError when sending mails --- cotisations/utils.py | 2 +- re2o/mail_utils.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cotisations/utils.py b/cotisations/utils.py index c370fe59..5da8eee0 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -50,7 +50,7 @@ def send_mail(mail, request): """Wrapper for Django's EmailMessage.send which handles errors""" try: mail.send() - except SMTPException as e: + except (SMTPException, ConnectionError) as e: if request: messages.error( request, diff --git a/re2o/mail_utils.py b/re2o/mail_utils.py index 065a9506..86d446fb 100644 --- a/re2o/mail_utils.py +++ b/re2o/mail_utils.py @@ -36,7 +36,7 @@ def send_mail(request, *args, **kwargs): try: kwargs["fail_silently"] = request is None django_send_mail(*args, **kwargs) - except SMTPException as e: + except (SMTPException, ConnectionError) as e: messages.error( request, _("Failed to send email: %(error)s.") % { From 6900c153d1f2f8981291c0e21ae8d9512cfba9c4 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 21:15:07 +0200 Subject: [PATCH 128/490] Move both send_mail utils to same file --- cotisations/utils.py | 22 +++------------------- re2o/mail_utils.py | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/cotisations/utils.py b/cotisations/utils.py index 5da8eee0..c7ca6187 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -23,9 +23,7 @@ import os from django.template.loader import get_template from django.core.mail import EmailMessage -from django.utils.translation import ugettext_lazy as _ -from django.contrib import messages -from smtplib import SMTPException +from re2o.mail_utils import send_mail_object from .tex import create_pdf from preferences.models import AssoOption, GeneralOption, CotisationsOption, Mandate @@ -46,20 +44,6 @@ def find_payment_method(payment): return None -def send_mail(mail, request): - """Wrapper for Django's EmailMessage.send which handles errors""" - try: - mail.send() - except (SMTPException, ConnectionError) as e: - if request: - messages.error( - request, - _("Failed to send email: %(error)s.") % { - "error": e, - }, - ) - - def send_mail_invoice(invoice, request=None): """Creates the pdf of the invoice and sends it by email to the client""" purchases_info = [] @@ -108,7 +92,7 @@ def send_mail_invoice(invoice, request=None): attachments=[("invoice.pdf", pdf, "application/pdf")], ) - send_mail(mail, request) + send_mail_object(mail, request) def send_mail_voucher(invoice, request=None): @@ -145,4 +129,4 @@ def send_mail_voucher(invoice, request=None): attachments=[("voucher.pdf", pdf, "application/pdf")], ) - send_mail(mail, request) + send_mail_object(mail, request) diff --git a/re2o/mail_utils.py b/re2o/mail_utils.py index 86d446fb..72dbac81 100644 --- a/re2o/mail_utils.py +++ b/re2o/mail_utils.py @@ -43,3 +43,17 @@ def send_mail(request, *args, **kwargs): "error": e, }, ) + + +def send_mail_object(mail, request): + """Wrapper for Django's EmailMessage.send which handles errors""" + try: + mail.send() + except (SMTPException, ConnectionError) as e: + if request: + messages.error( + request, + _("Failed to send email: %(error)s.") % { + "error": e, + }, + ) From 69286beabfe8bd6345e41ef07f782336dfbef4ac Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 19:19:06 +0000 Subject: [PATCH 129/490] Fix calls to invoice.save --- cotisations/models.py | 11 ++++++----- cotisations/views.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index 503ad244..dcb62408 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -329,11 +329,12 @@ class Facture(BaseInvoice): purchase.save() def save(self, *args, **kwargs): - super(Facture, self).save(*args, **kwargs) + try: + request = kwargs.pop("request") + except: + request = None - request = None - if "request" in kwargs: - request = kwargs["request"] + super(Facture, self).save(*args, **kwargs) if not self.__original_valid and self.valid: self.reorder_purchases() @@ -875,7 +876,7 @@ class Paiement(RevMixin, AclMixin, models.Model): # So make this invoice valid, trigger send mail invoice.valid = True - invoice.save(request) + invoice.save(request=request) # In case a cotisation was bought, inform the user, the # cotisation time has been extended too diff --git a/cotisations/views.py b/cotisations/views.py index def26cda..62eabef6 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -1008,7 +1008,7 @@ def credit_solde(request, user, **_kwargs): else: price_ok = True if price_ok: - invoice.save(request) + invoice.save(request=request) Vente.objects.create( facture=invoice, name="solde", From 8e3e2249a6cff3770b5c19522d746fe0cf63656f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 19 Apr 2020 20:06:33 +0000 Subject: [PATCH 130/490] Catch socket exceptions in mail_send --- re2o/mail_utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/re2o/mail_utils.py b/re2o/mail_utils.py index 72dbac81..69d0cfff 100644 --- a/re2o/mail_utils.py +++ b/re2o/mail_utils.py @@ -29,14 +29,14 @@ from django.utils.translation import ugettext_lazy as _ from django.core.mail import send_mail as django_send_mail from django.contrib import messages from smtplib import SMTPException - +from socket import herror, gaierror def send_mail(request, *args, **kwargs): """Wrapper for Django's send_mail which handles errors""" try: kwargs["fail_silently"] = request is None django_send_mail(*args, **kwargs) - except (SMTPException, ConnectionError) as e: + except (SMTPException, ConnectionError, herror, gaierror) as e: messages.error( request, _("Failed to send email: %(error)s.") % { @@ -49,7 +49,7 @@ def send_mail_object(mail, request): """Wrapper for Django's EmailMessage.send which handles errors""" try: mail.send() - except (SMTPException, ConnectionError) as e: + except (SMTPException, ConnectionError, herror, gaierror) as e: if request: messages.error( request, From 82e901b47d2dc0231535230a14280fec7838e33e Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 22:13:59 +0200 Subject: [PATCH 131/490] Regression fix, display email local state --- preferences/locale/fr/LC_MESSAGES/django.po | 197 ++++++++++-------- .../preferences/display_preferences.html | 14 ++ 2 files changed, 121 insertions(+), 90 deletions(-) diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index bf0387b0..84d7b67c 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -35,7 +35,7 @@ msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." #: preferences/forms.py:65 -#: preferences/templates/preferences/display_preferences.html:148 +#: preferences/templates/preferences/display_preferences.html:149 msgid "Telephone number required" msgstr "Numéro de téléphone requis" @@ -52,12 +52,12 @@ msgid "All can create a member" msgstr "Tous peuvent créer un adhérent" #: preferences/forms.py:69 -#: preferences/templates/preferences/display_preferences.html:134 +#: preferences/templates/preferences/display_preferences.html:135 msgid "Delay before disabling accounts without a verified email" msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" #: preferences/forms.py:70 -#: preferences/templates/preferences/display_preferences.html:120 +#: preferences/templates/preferences/display_preferences.html:121 msgid "Self registration" msgstr "Autoinscription" @@ -76,12 +76,12 @@ msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" #: preferences/forms.py:89 -#: preferences/templates/preferences/display_preferences.html:180 +#: preferences/templates/preferences/display_preferences.html:194 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" #: preferences/forms.py:92 -#: preferences/templates/preferences/display_preferences.html:184 +#: preferences/templates/preferences/display_preferences.html:198 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" @@ -146,7 +146,7 @@ msgid "Organisation name" msgstr "Nom de l'association" #: preferences/forms.py:169 -#: preferences/templates/preferences/display_preferences.html:313 +#: preferences/templates/preferences/display_preferences.html:327 msgid "SIRET number" msgstr "Numéro SIRET" @@ -159,17 +159,17 @@ msgid "Address (line 2)" msgstr "Adresse (ligne 2)" #: preferences/forms.py:172 -#: preferences/templates/preferences/display_preferences.html:321 +#: preferences/templates/preferences/display_preferences.html:335 msgid "Contact email address" msgstr "Adresse mail de contact" #: preferences/forms.py:173 -#: preferences/templates/preferences/display_preferences.html:325 +#: preferences/templates/preferences/display_preferences.html:339 msgid "Telephone number" msgstr "Numéro de téléphone" #: preferences/forms.py:174 -#: preferences/templates/preferences/display_preferences.html:327 +#: preferences/templates/preferences/display_preferences.html:341 msgid "Usual name" msgstr "Nom d'usage" @@ -199,7 +199,7 @@ msgid "Twitter URL" msgstr "URL du compte Twitter" #: preferences/forms.py:211 -#: preferences/templates/preferences/display_preferences.html:488 +#: preferences/templates/preferences/display_preferences.html:502 msgid "Twitter account name" msgstr "Nom du compte Twitter" @@ -226,7 +226,7 @@ msgstr "Les dates renseignées se superposent avec un mandat existant." #: preferences/forms.py:321 #: preferences/templates/preferences/aff_service.html:31 -#: preferences/templates/preferences/display_preferences.html:311 +#: preferences/templates/preferences/display_preferences.html:325 msgid "Name" msgstr "Nom" @@ -446,7 +446,7 @@ msgid "RADIUS key" msgstr "Clé RADIUS" #: preferences/models.py:385 -#: preferences/templates/preferences/display_preferences.html:207 +#: preferences/templates/preferences/display_preferences.html:221 msgid "RADIUS keys" msgstr "clés RADIUS" @@ -675,7 +675,7 @@ msgid "RADIUS policy" msgstr "politique de RADIUS" #: preferences/models.py:672 -#: preferences/templates/preferences/display_preferences.html:285 +#: preferences/templates/preferences/display_preferences.html:299 msgid "RADIUS policies" msgstr "politiques de RADIUS" @@ -848,7 +848,7 @@ msgid "File" msgstr "Fichier" #: preferences/templates/preferences/aff_mailcontact.html:31 -#: preferences/templates/preferences/display_preferences.html:317 +#: preferences/templates/preferences/display_preferences.html:331 msgid "Address" msgstr "Adresse" @@ -1028,12 +1028,12 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:46 #: preferences/templates/preferences/display_preferences.html:108 -#: preferences/templates/preferences/display_preferences.html:173 -#: preferences/templates/preferences/display_preferences.html:226 -#: preferences/templates/preferences/display_preferences.html:288 -#: preferences/templates/preferences/display_preferences.html:306 -#: preferences/templates/preferences/display_preferences.html:403 -#: preferences/templates/preferences/display_preferences.html:481 +#: preferences/templates/preferences/display_preferences.html:187 +#: preferences/templates/preferences/display_preferences.html:240 +#: preferences/templates/preferences/display_preferences.html:302 +#: preferences/templates/preferences/display_preferences.html:320 +#: preferences/templates/preferences/display_preferences.html:417 +#: preferences/templates/preferences/display_preferences.html:495 #: preferences/templates/preferences/edit_preferences.html:46 #: preferences/views.py:212 preferences/views.py:261 preferences/views.py:307 #: preferences/views.py:367 preferences/views.py:431 preferences/views.py:496 @@ -1062,6 +1062,7 @@ msgid "Local email accounts enabled" msgstr "Comptes mail locaux activés" #: preferences/templates/preferences/display_preferences.html:86 +#: preferences/templates/preferences/display_preferences.html:164 msgid "Local email domain" msgstr "Domaine de mail local" @@ -1073,269 +1074,285 @@ msgstr "Nombre maximum d'alias mail autorisé pour un utilisateur standard" msgid "User preferences" msgstr "Préférences d'utilisateur" -#: preferences/templates/preferences/display_preferences.html:114 +#: preferences/templates/preferences/display_preferences.html:112 +msgid "Accounts creation and self-register" +msgstr "" + +#: preferences/templates/preferences/display_preferences.html:115 msgid "Creation of members by everyone" msgstr "Création d'adhérents par tous" -#: preferences/templates/preferences/display_preferences.html:116 +#: preferences/templates/preferences/display_preferences.html:117 msgid "Creation of clubs by everyone" msgstr "Création de clubs par tous" -#: preferences/templates/preferences/display_preferences.html:122 +#: preferences/templates/preferences/display_preferences.html:123 msgid "Delete not yet active users after" msgstr "Suppression des utilisateurs n'ayant jamais adhéré après" -#: preferences/templates/preferences/display_preferences.html:123 +#: preferences/templates/preferences/display_preferences.html:124 #, python-format msgid "%(delete_notyetactive)s days" msgstr "%(delete_notyetactive)s jours" -#: preferences/templates/preferences/display_preferences.html:126 +#: preferences/templates/preferences/display_preferences.html:127 msgid "All users are active by default" msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/templates/preferences/display_preferences.html:128 +#: preferences/templates/preferences/display_preferences.html:129 msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" -#: preferences/templates/preferences/display_preferences.html:132 +#: preferences/templates/preferences/display_preferences.html:133 msgid "Allow directly entering a password during account creation" msgstr "" "Permettre le choix d'un mot de passe directement lors de la création du " "compte" -#: preferences/templates/preferences/display_preferences.html:135 +#: preferences/templates/preferences/display_preferences.html:136 #, fuzzy, python-format #| msgid "%(delete_notyetactive)s days" msgid "%(disable_emailnotyetconfirmed)s days" msgstr "%(delete_notyetactive)s jours" -#: preferences/templates/preferences/display_preferences.html:139 +#: preferences/templates/preferences/display_preferences.html:140 msgid "Users general permissions" msgstr "Permissions générales des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:142 +#: preferences/templates/preferences/display_preferences.html:143 msgid "Default shell for users" msgstr "Interface en ligne de commande par défaut pour les utilisateurs" -#: preferences/templates/preferences/display_preferences.html:144 +#: preferences/templates/preferences/display_preferences.html:145 msgid "Users can edit their shell" msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" -#: preferences/templates/preferences/display_preferences.html:150 +#: preferences/templates/preferences/display_preferences.html:151 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" -#: preferences/templates/preferences/display_preferences.html:154 +#: preferences/templates/preferences/display_preferences.html:155 msgid "Policy for self-user room change" msgstr "Autorisations pour le changement de chambre des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:165 +#: preferences/templates/preferences/display_preferences.html:159 +msgid "Local email accounts settings" +msgstr "Réglages des comptes email locaux" + +#: preferences/templates/preferences/display_preferences.html:162 +msgid "Local email accounts state" +msgstr "Etat des comptes email locaux" + +#: preferences/templates/preferences/display_preferences.html:168 +msgid "Maximum of local email address" +msgstr "Maximum de mail local autorisés" + +#: preferences/templates/preferences/display_preferences.html:179 msgid "Machine preferences" msgstr "Préférences de machine" -#: preferences/templates/preferences/display_preferences.html:178 +#: preferences/templates/preferences/display_preferences.html:192 msgid "Password per machine" msgstr "Mot de passe par machine" -#: preferences/templates/preferences/display_preferences.html:186 +#: preferences/templates/preferences/display_preferences.html:200 msgid "Default Time To Live (TTL) for CNAME, A and AAAA records." msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA." -#: preferences/templates/preferences/display_preferences.html:190 +#: preferences/templates/preferences/display_preferences.html:204 msgid "IPv6 support" msgstr "Support de l'IPv6" -#: preferences/templates/preferences/display_preferences.html:192 +#: preferences/templates/preferences/display_preferences.html:206 msgid "Creation of machines" msgstr "Création de machines" -#: preferences/templates/preferences/display_preferences.html:202 +#: preferences/templates/preferences/display_preferences.html:216 msgid "Topology preferences" msgstr "Préférences de topologie" -#: preferences/templates/preferences/display_preferences.html:209 +#: preferences/templates/preferences/display_preferences.html:223 msgid "Add a RADIUS key" msgstr "Ajouter une clé RADIUS" -#: preferences/templates/preferences/display_preferences.html:219 +#: preferences/templates/preferences/display_preferences.html:233 msgid "Configuration of switches" msgstr "Configuration de commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:232 +#: preferences/templates/preferences/display_preferences.html:246 msgid "Web management, activated in case of automatic provision" msgstr "Gestion web, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:234 +#: preferences/templates/preferences/display_preferences.html:248 msgid "REST management, activated in case of automatic provision" msgstr "Gestion REST, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:241 +#: preferences/templates/preferences/display_preferences.html:255 msgid "Provision of configuration for switches" msgstr "Provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:244 +#: preferences/templates/preferences/display_preferences.html:258 msgid "Switches with automatic provision" msgstr "Commutateurs réseau avec provision automatique" -#: preferences/templates/preferences/display_preferences.html:245 -#: preferences/templates/preferences/display_preferences.html:249 -#: preferences/templates/preferences/display_preferences.html:253 -#: preferences/templates/preferences/display_preferences.html:261 -#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:259 +#: preferences/templates/preferences/display_preferences.html:263 +#: preferences/templates/preferences/display_preferences.html:267 #: preferences/templates/preferences/display_preferences.html:275 +#: preferences/templates/preferences/display_preferences.html:279 +#: preferences/templates/preferences/display_preferences.html:289 msgid "OK" msgstr "OK" -#: preferences/templates/preferences/display_preferences.html:245 -#: preferences/templates/preferences/display_preferences.html:249 -#: preferences/templates/preferences/display_preferences.html:253 -#: preferences/templates/preferences/display_preferences.html:275 +#: preferences/templates/preferences/display_preferences.html:259 +#: preferences/templates/preferences/display_preferences.html:263 +#: preferences/templates/preferences/display_preferences.html:267 +#: preferences/templates/preferences/display_preferences.html:289 msgid "Missing" msgstr "Manquant" -#: preferences/templates/preferences/display_preferences.html:248 +#: preferences/templates/preferences/display_preferences.html:262 msgid "IP range for the management of switches" msgstr "Plage d'IP pour la gestion des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:252 +#: preferences/templates/preferences/display_preferences.html:266 msgid "Server for the configuration of switches" msgstr "Serveur pour la configuration des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:256 +#: preferences/templates/preferences/display_preferences.html:270 msgid "Provision of configuration mode for switches" msgstr "Mode de provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:260 +#: preferences/templates/preferences/display_preferences.html:274 msgid "TFTP mode" msgstr "Mode TFTP" -#: preferences/templates/preferences/display_preferences.html:264 +#: preferences/templates/preferences/display_preferences.html:278 msgid "SFTP mode" msgstr "Mode SFTP" -#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:279 msgid "Missing credentials" msgstr "Identifiants manquants" -#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:283 msgid "Switch management credentials" msgstr "Identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:271 +#: preferences/templates/preferences/display_preferences.html:285 msgid "Add switch management credentials" msgstr "Ajouter des identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:282 +#: preferences/templates/preferences/display_preferences.html:296 msgid "RADIUS preferences" msgstr "Préférences RADIUS" -#: preferences/templates/preferences/display_preferences.html:291 +#: preferences/templates/preferences/display_preferences.html:305 msgid "Current RADIUS attributes" msgstr "Attributs RADIUS actuels" -#: preferences/templates/preferences/display_preferences.html:292 +#: preferences/templates/preferences/display_preferences.html:306 msgid "Add an attribute" msgstr "Ajouter un attribut" -#: preferences/templates/preferences/display_preferences.html:300 +#: preferences/templates/preferences/display_preferences.html:314 msgid "Information about the organisation" msgstr "Informations sur l'association" -#: preferences/templates/preferences/display_preferences.html:331 +#: preferences/templates/preferences/display_preferences.html:345 msgid "User object of the organisation" msgstr "Objet utilisateur de l'association" -#: preferences/templates/preferences/display_preferences.html:333 +#: preferences/templates/preferences/display_preferences.html:347 msgid "Description of the organisation" msgstr "Description de l'association" -#: preferences/templates/preferences/display_preferences.html:337 +#: preferences/templates/preferences/display_preferences.html:351 msgid "Mandates" msgstr "Mandats" -#: preferences/templates/preferences/display_preferences.html:340 +#: preferences/templates/preferences/display_preferences.html:354 msgid "Add a mandate" msgstr "Ajouter un mandat" -#: preferences/templates/preferences/display_preferences.html:349 +#: preferences/templates/preferences/display_preferences.html:363 msgid "Document templates" msgstr "Modèles de document" -#: preferences/templates/preferences/display_preferences.html:355 +#: preferences/templates/preferences/display_preferences.html:369 msgid "Add a document template" msgstr "Ajouter un modèle de document" -#: preferences/templates/preferences/display_preferences.html:359 +#: preferences/templates/preferences/display_preferences.html:373 msgid "Delete one or several document templates" msgstr " Supprimer un ou plusieurs modèles de document" -#: preferences/templates/preferences/display_preferences.html:368 +#: preferences/templates/preferences/display_preferences.html:382 msgid "Subscription preferences" msgstr "Préférences de cotisation" -#: preferences/templates/preferences/display_preferences.html:377 +#: preferences/templates/preferences/display_preferences.html:391 msgid "Send voucher by email" msgstr "Envoyer le reçu par mail" -#: preferences/templates/preferences/display_preferences.html:381 +#: preferences/templates/preferences/display_preferences.html:395 msgid "Invoices' template" msgstr "Modèle des factures" -#: preferences/templates/preferences/display_preferences.html:385 +#: preferences/templates/preferences/display_preferences.html:399 msgid "Vouchers' template" msgstr "Modèle des reçus" -#: preferences/templates/preferences/display_preferences.html:396 +#: preferences/templates/preferences/display_preferences.html:410 msgid "Message for emails" msgstr "Message pour les mails" -#: preferences/templates/preferences/display_preferences.html:409 +#: preferences/templates/preferences/display_preferences.html:423 msgid "Welcome email (in French)" msgstr "Mail de bienvenue (en français)" -#: preferences/templates/preferences/display_preferences.html:413 +#: preferences/templates/preferences/display_preferences.html:427 msgid "Welcome email (in English)" msgstr "Mail de bienvenue (en anglais)" -#: preferences/templates/preferences/display_preferences.html:423 +#: preferences/templates/preferences/display_preferences.html:437 msgid "Preferences for the membership's end email" msgstr "Préférences pour le mail de fin d'adhésion" -#: preferences/templates/preferences/display_preferences.html:429 +#: preferences/templates/preferences/display_preferences.html:443 msgid "Add a reminder" msgstr "Ajouter un rappel" -#: preferences/templates/preferences/display_preferences.html:440 +#: preferences/templates/preferences/display_preferences.html:454 msgid "List of services and homepage preferences" msgstr "Liste des services et préférences de page d'accueil" -#: preferences/templates/preferences/display_preferences.html:446 +#: preferences/templates/preferences/display_preferences.html:460 msgid "Add a service" msgstr "Ajouter un service" -#: preferences/templates/preferences/display_preferences.html:457 +#: preferences/templates/preferences/display_preferences.html:471 msgid "List of contact email addresses" msgstr "Liste des adresses mail de contact" -#: preferences/templates/preferences/display_preferences.html:463 +#: preferences/templates/preferences/display_preferences.html:477 msgid "Add an address" msgstr "Ajouter une adresse" -#: preferences/templates/preferences/display_preferences.html:465 +#: preferences/templates/preferences/display_preferences.html:479 msgid "Delete one or several addresses" msgstr "Supprimer une ou plusieurs adresses" -#: preferences/templates/preferences/display_preferences.html:474 +#: preferences/templates/preferences/display_preferences.html:488 msgid "Social networks" msgstr "Réseaux sociaux" -#: preferences/templates/preferences/display_preferences.html:486 +#: preferences/templates/preferences/display_preferences.html:500 msgid "Twitter account URL" msgstr "URL du compte Twitter" -#: preferences/templates/preferences/display_preferences.html:492 +#: preferences/templates/preferences/display_preferences.html:506 msgid "Facebook account URL" msgstr "URL du compte Facebook" diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 2142eb68..463a4d90 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -109,6 +109,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

+

{% trans "Accounts creation and self-register" %}

@@ -155,6 +156,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% trans "Creation of members by everyone" %}{{ useroptions.self_room_policy }}
+

{% trans "Local email accounts settings" %}

+ + + + + + + + + + + +
{% trans "Local email accounts state" %}{{ useroptions.local_email_accounts_enabled|tick }}{% trans "Local email domain" %}{{ useroptions.local_email_domain }}
{% trans "Maximum of local email address" %}{{ useroptions.max_email_address }}
From 2cf3c3ce19d7d432e172c52ebd36becb602979f8 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 22:14:38 +0200 Subject: [PATCH 132/490] Validate password using django settings password validator --- re2o/settings_local.example.py | 3 +++ users/forms.py | 23 ++++------------------- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index c4ec9ff2..7130a1f2 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -106,3 +106,6 @@ OPTIONNAL_APPS_RE2O = () # Some Django apps you want to add in you local project OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + () + +#Set auth password validator +AUTH_PASSWORD_VALIDATORS = [] diff --git a/users/forms.py b/users/forms.py index e4f39f10..8e946c23 100644 --- a/users/forms.py +++ b/users/forms.py @@ -38,6 +38,7 @@ from __future__ import unicode_literals from django import forms from django.forms import ModelForm, Form from django.contrib.auth.forms import ReadOnlyPasswordHashField +from django.contrib.auth.password_validation import validate_password from django.core.validators import MinLengthValidator from django.utils import timezone from django.utils.functional import lazy @@ -82,13 +83,11 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): passwd1 = forms.CharField( label=_("New password"), max_length=255, - validators=[MinLengthValidator(8)], widget=forms.PasswordInput, ) passwd2 = forms.CharField( label=_("New password confirmation"), max_length=255, - validators=[MinLengthValidator(8)], widget=forms.PasswordInput, ) @@ -103,6 +102,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): password2 = self.cleaned_data.get("passwd2") if password1 and password2 and password1 != password2: raise forms.ValidationError(_("The new passwords don't match.")) + validate_password(password1, user=self.instance) return password2 def clean_selfpasswd(self): @@ -131,13 +131,11 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): password1 = forms.CharField( label=_("Password"), widget=forms.PasswordInput, - validators=[MinLengthValidator(8)], max_length=255, ) password2 = forms.CharField( label=_("Password confirmation"), widget=forms.PasswordInput, - validators=[MinLengthValidator(8)], max_length=255, ) is_admin = forms.BooleanField(label=_("Is admin")) @@ -167,6 +165,7 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError(_("The passwords don't match.")) + validate_password(password1) return password2 def save(self, commit=True): @@ -424,14 +423,12 @@ class AdherentCreationForm(AdherentForm): required=False, label=_("Password"), widget=forms.PasswordInput, - #validators=[MinLengthValidator(8)], max_length=255, ) password2 = forms.CharField( required=False, label=_("Password confirmation"), widget=forms.PasswordInput, - #validators=[MinLengthValidator(8)], max_length=255, ) @@ -481,18 +478,6 @@ class AdherentCreationForm(AdherentForm): self.fields.pop("password1") self.fields.pop("password2") - def clean_password1(self): - """Ignore ce champs si la case init_password_by_mail est décochée""" - send_email = self.cleaned_data.get("init_password_by_mail") - if send_email: - return None - - password1 = self.cleaned_data.get("password1") - if len(password1) < 8: - raise forms.ValidationError(_("Password must contain at least 8 characters.")) - - return password1 - def clean_password2(self): """Verifie que password1 et 2 sont identiques (si nécessaire)""" send_email = self.cleaned_data.get("init_password_by_mail") @@ -504,7 +489,7 @@ class AdherentCreationForm(AdherentForm): password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError(_("The passwords don't match.")) - + validate_password(password1) return password2 def save(self, commit=True): From c19723375bdf9c0a452430164ddf97f7168a00d0 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 22:20:13 +0200 Subject: [PATCH 133/490] Enforce password policy example file --- re2o/settings_local.example.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index 7130a1f2..18c80559 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -108,4 +108,20 @@ OPTIONNAL_APPS_RE2O = () OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + () #Set auth password validator -AUTH_PASSWORD_VALIDATORS = [] +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + 'OPTIONS': { + 'min_length': 8, + } + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] From 0d7ca44edd9e7acd7fe7ba6eb3ca8e4beb4ae531 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 22:40:49 +0200 Subject: [PATCH 134/490] Add help_text for password --- users/forms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/users/forms.py b/users/forms.py index 8e946c23..3440f2f9 100644 --- a/users/forms.py +++ b/users/forms.py @@ -38,7 +38,7 @@ from __future__ import unicode_literals from django import forms from django.forms import ModelForm, Form from django.contrib.auth.forms import ReadOnlyPasswordHashField -from django.contrib.auth.password_validation import validate_password +from django.contrib.auth.password_validation import validate_password, password_validators_help_text_html from django.core.validators import MinLengthValidator from django.utils import timezone from django.utils.functional import lazy @@ -84,6 +84,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): label=_("New password"), max_length=255, widget=forms.PasswordInput, + help_text=password_validators_help_text_html() ) passwd2 = forms.CharField( label=_("New password confirmation"), @@ -132,6 +133,7 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): label=_("Password"), widget=forms.PasswordInput, max_length=255, + help_text=password_validators_help_text_html() ) password2 = forms.CharField( label=_("Password confirmation"), @@ -424,6 +426,7 @@ class AdherentCreationForm(AdherentForm): label=_("Password"), widget=forms.PasswordInput, max_length=255, + help_text=password_validators_help_text_html() ) password2 = forms.CharField( required=False, From c775d9ba5162d2986c6ba984327a5466ab06a98e Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 23:02:50 +0200 Subject: [PATCH 135/490] Give to django main user personnal fields --- re2o/settings_local.example.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index 18c80559..d0de4709 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -111,6 +111,9 @@ OPTIONNAL_APPS = OPTIONNAL_APPS_RE2O + () AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + 'OPTIONS': { + 'user_attributes': ['surname', 'pseudo', 'name', 'email'], + } }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', From 334537e475511536a9f54dc8f0bd927581933c95 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sun, 19 Apr 2020 23:40:09 +0200 Subject: [PATCH 136/490] Remove old api --- machines/serializers.py | 442 ---------------------------------------- machines/urls.py | 12 -- machines/views.py | 250 +---------------------- users/serializers.py | 49 ----- users/urls.py | 17 -- users/views.py | 76 ------- 6 files changed, 1 insertion(+), 845 deletions(-) delete mode 100644 machines/serializers.py delete mode 100644 users/serializers.py diff --git a/machines/serializers.py b/machines/serializers.py deleted file mode 100644 index 95e0df5c..00000000 --- a/machines/serializers.py +++ /dev/null @@ -1,442 +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 Lara 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. - -# Augustin Lemesle -"""machines.serializers -Serializers for the Machines app -""" - -from rest_framework import serializers - -from machines.models import ( - Interface, - IpType, - Extension, - IpList, - Domain, - Txt, - Mx, - Srv, - Service_link, - Ns, - OuverturePort, - Ipv6List, -) - - -class IpTypeField(serializers.RelatedField): - """ Serializer for an IpType object field """ - - def to_representation(self, value): - return str(value) - - def to_internal_value(self, data): - pass - - -class IpListSerializer(serializers.ModelSerializer): - """ Serializer for an Ipv4List obejct using the IpType serialization """ - - ip_type = IpTypeField(read_only=True) - - class Meta: - model = IpList - fields = ("ipv4", "ip_type") - - -class Ipv6ListSerializer(serializers.ModelSerializer): - """ Serializer for an Ipv6List object """ - - class Meta: - model = Ipv6List - fields = ("ipv6", "slaac_ip") - - -class InterfaceSerializer(serializers.ModelSerializer): - """ Serializer for an Interface object. Use SerializerMethodField - to get ForeignKey values """ - - ipv4 = IpListSerializer(read_only=True) - # TODO : use serializer.RelatedField to avoid duplicate code - mac_address = serializers.SerializerMethodField("get_macaddress") - domain = serializers.SerializerMethodField("get_dns") - extension = serializers.SerializerMethodField("get_interface_extension") - - class Meta: - model = Interface - fields = ("ipv4", "mac_address", "domain", "extension") - - @staticmethod - def get_dns(obj): - """ The name of the associated DNS object """ - return obj.domain.name - - @staticmethod - def get_interface_extension(obj): - """ The name of the associated Interface object """ - return obj.domain.extension.name - - @staticmethod - def get_macaddress(obj): - """ The string representation of the associated MAC address """ - return str(obj.mac_address) - - -class FullInterfaceSerializer(serializers.ModelSerializer): - """ Serializer for an Interface obejct. Use SerializerMethodField - to get ForeignKey values """ - - ipv4 = IpListSerializer(read_only=True) - ipv6 = Ipv6ListSerializer(read_only=True, many=True) - # TODO : use serializer.RelatedField to avoid duplicate code - mac_address = serializers.SerializerMethodField("get_macaddress") - domain = serializers.SerializerMethodField("get_dns") - extension = serializers.SerializerMethodField("get_interface_extension") - - class Meta: - model = Interface - fields = ("ipv4", "ipv6", "mac_address", "domain", "extension") - - @staticmethod - def get_dns(obj): - """ The name of the associated DNS object """ - return obj.domain.name - - @staticmethod - def get_interface_extension(obj): - """ The name of the associated Extension object """ - return obj.domain.extension.name - - @staticmethod - def get_macaddress(obj): - """ The string representation of the associated MAC address """ - return str(obj.mac_address) - - -class ExtensionNameField(serializers.RelatedField): - """ Serializer for Extension object field """ - - def to_representation(self, value): - return value.name - - def to_internal_value(self, data): - pass - - -class TypeSerializer(serializers.ModelSerializer): - """ Serializer for an IpType object. Use SerializerMethodField to - get ForeignKey values. Infos about the general port policy is added """ - - extension = ExtensionNameField(read_only=True) - ouverture_ports_tcp_in = serializers.SerializerMethodField( - "get_port_policy_input_tcp" - ) - ouverture_ports_tcp_out = serializers.SerializerMethodField( - "get_port_policy_output_tcp" - ) - ouverture_ports_udp_in = serializers.SerializerMethodField( - "get_port_policy_input_udp" - ) - ouverture_ports_udp_out = serializers.SerializerMethodField( - "get_port_policy_output_udp" - ) - - class Meta: - model = IpType - fields = ( - "name", - "extension", - "domaine_ip_start", - "domaine_ip_stop", - "prefix_v6", - "ouverture_ports_tcp_in", - "ouverture_ports_tcp_out", - "ouverture_ports_udp_in", - "ouverture_ports_udp_out", - ) - - @staticmethod - def get_port_policy(obj, protocole, io): - """ Generic utility function to get the policy for a given - port, protocole and IN or OUT """ - if obj.ouverture_ports is None: - return [] - return map( - str, - obj.ouverture_ports.ouvertureport_set.filter(protocole=protocole).filter( - io=io - ), - ) - - def get_port_policy_input_tcp(self, obj): - """Renvoie la liste des ports ouverts en entrée tcp""" - return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.IN) - - def get_port_policy_output_tcp(self, obj): - """Renvoie la liste des ports ouverts en sortie tcp""" - return self.get_port_policy(obj, OuverturePort.TCP, OuverturePort.OUT) - - def get_port_policy_input_udp(self, obj): - """Renvoie la liste des ports ouverts en entrée udp""" - return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.IN) - - def get_port_policy_output_udp(self, obj): - """Renvoie la liste des ports ouverts en sortie udp""" - return self.get_port_policy(obj, OuverturePort.UDP, OuverturePort.OUT) - - -class ExtensionSerializer(serializers.ModelSerializer): - """Serialisation d'une extension : origin_ip et la zone sont - des foreign_key donc evalués en get_...""" - - origin = serializers.SerializerMethodField("get_origin_ip") - zone_entry = serializers.SerializerMethodField("get_zone_name") - soa = serializers.SerializerMethodField("get_soa_data") - - class Meta: - model = Extension - fields = ("name", "origin", "origin_v6", "zone_entry", "soa") - - @staticmethod - def get_origin_ip(obj): - """ The IP of the associated origin for the zone """ - return obj.origin.ipv4 - - @staticmethod - def get_zone_name(obj): - """ The name of the associated zone """ - return str(obj.dns_entry) - - @staticmethod - def get_soa_data(obj): - """ The representation of the associated SOA """ - return {"mail": obj.soa.dns_soa_mail, "param": obj.soa.dns_soa_param} - - -class MxSerializer(serializers.ModelSerializer): - """Serialisation d'un MX, evaluation du nom, de la zone - et du serveur cible, etant des foreign_key""" - - name = serializers.SerializerMethodField("get_entry_name") - zone = serializers.SerializerMethodField("get_zone_name") - mx_entry = serializers.SerializerMethodField("get_mx_name") - - class Meta: - model = Mx - fields = ("zone", "priority", "name", "mx_entry") - - @staticmethod - def get_entry_name(obj): - """ The name of the DNS MX entry """ - return str(obj.name) - - @staticmethod - def get_zone_name(obj): - """ The name of the associated zone of the MX record """ - return obj.zone.name - - @staticmethod - def get_mx_name(obj): - """ The string representation of the entry to add to the DNS """ - return str(obj.dns_entry) - - -class TxtSerializer(serializers.ModelSerializer): - """Serialisation d'un txt : zone cible et l'entrée txt - sont evaluées à part""" - - zone = serializers.SerializerMethodField("get_zone_name") - txt_entry = serializers.SerializerMethodField("get_txt_name") - - class Meta: - model = Txt - fields = ("zone", "txt_entry", "field1", "field2") - - @staticmethod - def get_zone_name(obj): - """ The name of the associated zone """ - return str(obj.zone.name) - - @staticmethod - def get_txt_name(obj): - """ The string representation of the entry to add to the DNS """ - return str(obj.dns_entry) - - -class SrvSerializer(serializers.ModelSerializer): - """Serialisation d'un srv : zone cible et l'entrée txt""" - - extension = serializers.SerializerMethodField("get_extension_name") - srv_entry = serializers.SerializerMethodField("get_srv_name") - - class Meta: - model = Srv - fields = ( - "service", - "protocole", - "extension", - "ttl", - "priority", - "weight", - "port", - "target", - "srv_entry", - ) - - @staticmethod - def get_extension_name(obj): - """ The name of the associated extension """ - return str(obj.extension.name) - - @staticmethod - def get_srv_name(obj): - """ The string representation of the entry to add to the DNS """ - return str(obj.dns_entry) - - -class NsSerializer(serializers.ModelSerializer): - """Serialisation d'un NS : la zone, l'entrée ns complète et le serveur - ns sont évalués à part""" - - zone = serializers.SerializerMethodField("get_zone_name") - ns = serializers.SerializerMethodField("get_domain_name") - ns_entry = serializers.SerializerMethodField("get_text_name") - - class Meta: - model = Ns - fields = ("zone", "ns", "ns_entry") - - @staticmethod - def get_zone_name(obj): - """ The name of the associated zone """ - return obj.zone.name - - @staticmethod - def get_domain_name(obj): - """ The name of the associated NS target """ - return str(obj.ns) - - @staticmethod - def get_text_name(obj): - """ The string representation of the entry to add to the DNS """ - return str(obj.dns_entry) - - -class DomainSerializer(serializers.ModelSerializer): - """Serialisation d'un domain, extension, cname sont des foreign_key, - et l'entrée complète, sont évalués à part""" - - extension = serializers.SerializerMethodField("get_zone_name") - cname = serializers.SerializerMethodField("get_alias_name") - cname_entry = serializers.SerializerMethodField("get_cname_name") - - class Meta: - model = Domain - fields = ("name", "extension", "cname", "cname_entry") - - @staticmethod - def get_zone_name(obj): - """ The name of the associated zone """ - return obj.extension.name - - @staticmethod - def get_alias_name(obj): - """ The name of the associated alias """ - return str(obj.cname) - - @staticmethod - def get_cname_name(obj): - """ The name of the associated CNAME target """ - return str(obj.dns_entry) - - -class ServiceServersSerializer(serializers.ModelSerializer): - """Evaluation d'un Service, et serialisation""" - - server = serializers.SerializerMethodField("get_server_name") - service = serializers.SerializerMethodField("get_service_name") - need_regen = serializers.SerializerMethodField("get_regen_status") - - class Meta: - model = Service_link - fields = ("server", "service", "need_regen") - - @staticmethod - def get_server_name(obj): - """ The name of the associated server """ - return str(obj.server.domain.name) - - @staticmethod - def get_service_name(obj): - """ The name of the service name """ - return str(obj.service) - - @staticmethod - def get_regen_status(obj): - """ The string representation of the regen status """ - return obj.need_regen - - -class OuverturePortsSerializer(serializers.Serializer): - """Serialisation de l'ouverture des ports""" - - ipv4 = serializers.SerializerMethodField() - ipv6 = serializers.SerializerMethodField() - - def create(self, validated_data): - """ Creates a new object based on the un-serialized data. - Used to implement an abstract inherited method """ - pass - - def update(self, instance, validated_data): - """ Updates an object based on the un-serialized data. - Used to implement an abstract inherited method """ - pass - - @staticmethod - def get_ipv4(): - """ The representation of the policy for the IPv4 addresses """ - return { - i.ipv4.ipv4: { - "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out": [j.tcp_ports_out() for j in i.port_lists.all()], - "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], - "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], - } - for i in Interface.objects.all() - if i.ipv4 - } - - @staticmethod - def get_ipv6(): - """ The representation of the policy for the IPv6 addresses """ - return { - i.ipv6: { - "tcp_in": [j.tcp_ports_in() for j in i.port_lists.all()], - "tcp_out": [j.tcp_ports_out() for j in i.port_lists.all()], - "udp_in": [j.udp_ports_in() for j in i.port_lists.all()], - "udp_out": [j.udp_ports_out() for j in i.port_lists.all()], - } - for i in Interface.objects.all() - if i.ipv6 - } diff --git a/machines/urls.py b/machines/urls.py index 8fb08d99..3b7b77b9 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -136,18 +136,6 @@ urlpatterns = [ url(r"^del_nas/$", views.del_nas, name="del-nas"), url(r"^index_nas/$", views.index_nas, name="index-nas"), url(r"^$", views.index, name="index"), - url(r"^rest/mac-ip/$", views.mac_ip, name="mac-ip"), - url(r"^rest/regen-achieved/$", views.regen_achieved, name="regen-achieved"), - url(r"^rest/mac-ip-dns/$", views.mac_ip_dns, name="mac-ip-dns"), - url(r"^rest/alias/$", views.alias, name="alias"), - url(r"^rest/corresp/$", views.corresp, name="corresp"), - url(r"^rest/mx/$", views.mx, name="mx"), - url(r"^rest/ns/$", views.ns, name="ns"), - url(r"^rest/txt/$", views.txt, name="txt"), - url(r"^rest/srv/$", views.srv, name="srv"), - url(r"^rest/zones/$", views.zones, name="zones"), - url(r"^rest/service_servers/$", views.service_servers, name="service-servers"), - url(r"^rest/ouverture_ports/$", views.ouverture_ports, name="ouverture-ports"), url(r"index_portlist/$", views.index_portlist, name="index-portlist"), url( r"^edit_portlist/(?P[0-9]+)$", diff --git a/machines/views.py b/machines/views.py index 403389d2..3291adb3 100644 --- a/machines/views.py +++ b/machines/views.py @@ -120,18 +120,7 @@ from .models import ( OuverturePort, Ipv6List, ) -from .serializers import ( - FullInterfaceSerializer, - InterfaceSerializer, - TypeSerializer, - DomainSerializer, - TxtSerializer, - SrvSerializer, - MxSerializer, - ExtensionSerializer, - ServiceServersSerializer, - NsSerializer, -) + def f_type_id(is_type_tt): @@ -1577,240 +1566,3 @@ def configure_ports(request, interface_instance, **_kwargs): request, ) - -# Framework Rest - - -class JSONResponse(HttpResponse): - """ Class to build a JSON response. Used for API """ - - def __init__(self, data, **kwargs): - content = JSONRenderer().render(data) - kwargs["content_type"] = "application/json" - super(JSONResponse, self).__init__(content, **kwargs) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def mac_ip_list(_request): - """ API view to list the active and assigned interfaces """ - interfaces = all_active_assigned_interfaces() - seria = InterfaceSerializer(interfaces, many=True) - return seria.data - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def full_mac_ip_list(_request): - """ API view to list the active and assigned interfaces. More - detailed than mac_ip_list(request) """ - interfaces = all_active_assigned_interfaces(full=True) - seria = FullInterfaceSerializer(interfaces, many=True) - return seria.data - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def alias(_request): - """ API view to list the alias (CNAME) for all assigned interfaces """ - alias = ( - Domain.objects.filter(interface_parent=None) - .filter( - cname__in=Domain.objects.filter( - interface_parent__in=Interface.objects.exclude(ipv4=None) - ) - ) - .select_related("extension") - .select_related("cname__extension") - ) - seria = DomainSerializer(alias, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def corresp(_request): - """ API view to list the types of IP and infos about it """ - type = IpType.objects.all().select_related("extension") - seria = TypeSerializer(type, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def mx(_request): - """ API view to list the MX records """ - mx = Mx.objects.all().select_related("zone").select_related("name__extension") - seria = MxSerializer(mx, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def txt(_request): - """ API view to list the TXT records """ - txt = Txt.objects.all().select_related("zone") - seria = TxtSerializer(txt, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def srv(_request): - """ API view to list the SRV records """ - srv = ( - Srv.objects.all() - .select_related("extension") - .select_related("target__extension") - ) - seria = SrvSerializer(srv, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ns(_request): - """ API view to list the NS records """ - ns = ( - Ns.objects.exclude( - ns__in=Domain.objects.filter( - interface_parent__in=Interface.objects.filter(ipv4=None) - ) - ) - .select_related("zone") - .select_related("ns__extension") - ) - seria = NsSerializer(ns, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def zones(_request): - """ API view to list the DNS zones """ - zones = Extension.objects.all().select_related("origin") - seria = ExtensionSerializer(zones, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def mac_ip(request): - """ API view to list the active and assigned interfaces """ - seria = mac_ip_list(request) - return JSONResponse(seria) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def mac_ip_dns(request): - """ API view to list the active and assigned interfaces. More - detailed than mac_ip_list(request) """ - seria = full_mac_ip_list(request) - return JSONResponse(seria) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def service_servers(_request): - """ API view to list the service links """ - service_link = ( - Service_link.objects.all() - .select_related("server__domain") - .select_related("service") - ) - seria = ServiceServersSerializer(service_link, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ouverture_ports(_request): - """ API view to list the port policies for each IP """ - r = {"ipv4": {}, "ipv6": {}} - for o in ( - OuverturePortList.objects.all() - .prefetch_related("ouvertureport_set") - .prefetch_related("interface_set", "interface_set__ipv4") - ): - pl = { - "tcp_in": set( - map( - str, - o.ouvertureport_set.filter( - protocole=OuverturePort.TCP, io=OuverturePort.IN - ), - ) - ), - "tcp_out": set( - map( - str, - o.ouvertureport_set.filter( - protocole=OuverturePort.TCP, io=OuverturePort.OUT - ), - ) - ), - "udp_in": set( - map( - str, - o.ouvertureport_set.filter( - protocole=OuverturePort.UDP, io=OuverturePort.IN - ), - ) - ), - "udp_out": set( - map( - str, - o.ouvertureport_set.filter( - protocole=OuverturePort.UDP, io=OuverturePort.OUT - ), - ) - ), - } - for i in filter_active_interfaces(o.interface_set): - if i.may_have_port_open(): - d = r["ipv4"].get(i.ipv4.ipv4, {}) - d["tcp_in"] = d.get("tcp_in", set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out", set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in", set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out", set()).union(pl["udp_out"]) - r["ipv4"][i.ipv4.ipv4] = d - if i.ipv6(): - for ipv6 in i.ipv6(): - d = r["ipv6"].get(ipv6.ipv6, {}) - d["tcp_in"] = d.get("tcp_in", set()).union(pl["tcp_in"]) - d["tcp_out"] = d.get("tcp_out", set()).union(pl["tcp_out"]) - d["udp_in"] = d.get("udp_in", set()).union(pl["udp_in"]) - d["udp_out"] = d.get("udp_out", set()).union(pl["udp_out"]) - r["ipv6"][ipv6.ipv6] = d - return JSONResponse(r) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def regen_achieved(request): - """ API view to list the regen status for each (Service link, Server) - couple """ - obj = Service_link.objects.filter( - service__in=Service.objects.filter(service_type=request.POST["service"]), - server__in=Interface.objects.filter( - domain__in=Domain.objects.filter(name=request.POST["server"]) - ), - ) - if obj: - obj.first().done_regen() - return HttpResponse("Ok") diff --git a/users/serializers.py b/users/serializers.py deleted file mode 100644 index 65ac7ef1..00000000 --- a/users/serializers.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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 Lara 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. - -# Maël Kervella - -"""users.serializers -Serializers for the User app -""" - -from rest_framework import serializers -from users.models import Club, Adherent - - -class MailingSerializer(serializers.ModelSerializer): - """ Serializer to build Mailing objects """ - - name = serializers.CharField(source="pseudo") - - class Meta: - model = Club - fields = ("name",) - - -class MailingMemberSerializer(serializers.ModelSerializer): - """ Serializer fot the Adherent objects (who belong to a - Mailing) """ - - class Meta: - model = Adherent - fields = ("email",) diff --git a/users/urls.py b/users/urls.py index a1579a29..9ecb3967 100644 --- a/users/urls.py +++ b/users/urls.py @@ -127,21 +127,4 @@ urlpatterns = [ url(r"^$", views.index, name="index"), url(r"^index_clubs/$", views.index_clubs, name="index-clubs"), url(r"^initial_register/$", views.initial_register, name="initial-register"), - url(r"^rest/ml/std/$", views.ml_std_list, name="ml-std-list"), - url( - r"^rest/ml/std/member/(?P\w+)/$", - views.ml_std_members, - name="ml-std-members", - ), - url(r"^rest/ml/club/$", views.ml_club_list, name="ml-club-list"), - url( - r"^rest/ml/club/admin/(?P\w+)/$", - views.ml_club_admins, - name="ml-club-admins", - ), - url( - r"^rest/ml/club/member/(?P\w+)/$", - views.ml_club_members, - name="ml-club-members", - ), ] diff --git a/users/views.py b/users/views.py index 1c544ba6..f37b9648 100644 --- a/users/views.py +++ b/users/views.py @@ -74,7 +74,6 @@ from re2o.acl import ( ) from cotisations.utils import find_payment_method from topologie.models import Port -from .serializers import MailingSerializer, MailingMemberSerializer from .models import ( User, Ban, @@ -1129,78 +1128,3 @@ def initial_register(request): request, ) - -class JSONResponse(HttpResponse): - """ Framework Rest """ - - def __init__(self, data, **kwargs): - content = JSONRenderer().render(data) - kwargs["content_type"] = "application/json" - super(JSONResponse, self).__init__(content, **kwargs) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ml_std_list(_request): - """ API view sending all the available standard mailings""" - return JSONResponse([{"name": "adherents"}]) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ml_std_members(request, ml_name): - """ API view sending all the members for a standard mailing""" - # All with active connextion - if ml_name == "adherents": - members = all_has_access().values("email").distinct() - # Unknown mailing - else: - messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse("index")) - seria = MailingMemberSerializer(members, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ml_club_list(_request): - """ API view sending all the available club mailings""" - clubs = Club.objects.filter(mailing=True).values("pseudo") - seria = MailingSerializer(clubs, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ml_club_admins(request, ml_name): - """ API view sending all the administrators for a specific club mailing""" - try: - club = Club.objects.get(mailing=True, pseudo=ml_name) - except Club.DoesNotExist: - messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse("index")) - members = club.administrators.all().values("email").distinct() - seria = MailingMemberSerializer(members, many=True) - return JSONResponse(seria.data) - - -@csrf_exempt -@login_required -@permission_required("machines.serveur") -def ml_club_members(request, ml_name): - """ API view sending all the members for a specific club mailing""" - try: - club = Club.objects.get(mailing=True, pseudo=ml_name) - except Club.DoesNotExist: - messages.error(request, _("The mailing list doesn't exist.")) - return redirect(reverse("index")) - members = ( - club.administrators.all().values("email").distinct() - | club.members.all().values("email").distinct() - ) - seria = MailingMemberSerializer(members, many=True) - return JSONResponse(seria.data) From 0b58365c872afeded8d5935e6313bf26d62ddfa7 Mon Sep 17 00:00:00 2001 From: chapeau Date: Mon, 20 Apr 2020 20:43:11 +0200 Subject: [PATCH 137/490] First draft of moving api functions into each apps --- api/serializers.py | 1352 -------------------------------- api/urls.py | 125 +-- api/views.py | 733 +---------------- cotisations/api/__init__.py | 0 cotisations/api/serializers.py | 105 +++ cotisations/api/urls.py | 35 + cotisations/api/views.py | 80 ++ machines/api/__init__.py | 0 machines/api/serializers.py | 592 ++++++++++++++ machines/api/urls.py | 66 ++ machines/api/views.py | 267 +++++++ preferences/api/__init__.py | 0 preferences/api/serializers.py | 174 ++++ preferences/api/urls.py | 37 + preferences/api/views.py | 130 +++ topologie/api/__init__.py | 0 topologie/api/serializers.py | 323 ++++++++ topologie/api/urls.py | 46 ++ topologie/api/views.py | 149 ++++ users/api/__init__.py | 0 users/api/serializers.py | 244 ++++++ users/api/urls.py | 49 ++ users/api/views.py | 191 +++++ 23 files changed, 2513 insertions(+), 2185 deletions(-) create mode 100644 cotisations/api/__init__.py create mode 100644 cotisations/api/serializers.py create mode 100644 cotisations/api/urls.py create mode 100644 cotisations/api/views.py create mode 100644 machines/api/__init__.py create mode 100644 machines/api/serializers.py create mode 100644 machines/api/urls.py create mode 100644 machines/api/views.py create mode 100644 preferences/api/__init__.py create mode 100644 preferences/api/serializers.py create mode 100644 preferences/api/urls.py create mode 100644 preferences/api/views.py create mode 100644 topologie/api/__init__.py create mode 100644 topologie/api/serializers.py create mode 100644 topologie/api/urls.py create mode 100644 topologie/api/views.py create mode 100644 users/api/__init__.py create mode 100644 users/api/serializers.py create mode 100644 users/api/urls.py create mode 100644 users/api/views.py diff --git a/api/serializers.py b/api/serializers.py index 6a40d525..6419d9d2 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -24,10 +24,7 @@ from rest_framework import serializers -import cotisations.models as cotisations -import machines.models as machines import preferences.models as preferences -import topologie.models as topologie import users.models as users # The namespace used for the API. It must match the namespace used in the @@ -66,1301 +63,6 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer): serializer_url_field = NamespacedHIField -# COTISATIONS - - -class FactureSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Facture` objects. - """ - - class Meta: - model = cotisations.Facture - fields = ( - "user", - "paiement", - "banque", - "cheque", - "date", - "valid", - "control", - "prix_total", - "name", - "api_url", - ) - - -class BaseInvoiceSerializer(NamespacedHMSerializer): - class Meta: - model = cotisations.BaseInvoice - fields = "__all__" - - -class VenteSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Vente` objects. - """ - - class Meta: - model = cotisations.Vente - fields = ( - "facture", - "number", - "name", - "prix", - "duration", - "type_cotisation", - "prix_total", - "api_url", - ) - - -class ArticleSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Article` objects. - """ - - class Meta: - model = cotisations.Article - fields = ("name", "prix", "duration", "type_user", "type_cotisation", "api_url") - - -class BanqueSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Banque` objects. - """ - - class Meta: - model = cotisations.Banque - fields = ("name", "api_url") - - -class PaiementSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Paiement` objects. - """ - - class Meta: - model = cotisations.Paiement - fields = ("moyen", "api_url") - - -class CotisationSerializer(NamespacedHMSerializer): - """Serialize `cotisations.models.Cotisation` objects. - """ - - class Meta: - model = cotisations.Cotisation - fields = ("vente", "type_cotisation", "date_start", "date_end", "api_url") - - -# MACHINES - - -class MachineSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Machine` objects. - """ - - class Meta: - model = machines.Machine - fields = ("user", "name", "active", "api_url") - - -class MachineTypeSerializer(NamespacedHMSerializer): - """Serialize `machines.models.MachineType` objects. - """ - - class Meta: - model = machines.MachineType - fields = ("name", "ip_type", "api_url") - - -class IpTypeSerializer(NamespacedHMSerializer): - """Serialize `machines.models.IpType` objects. - """ - - class Meta: - model = machines.IpType - fields = ( - "name", - "extension", - "need_infra", - "domaine_ip_start", - "domaine_ip_stop", - "prefix_v6", - "vlan", - "ouverture_ports", - "api_url", - ) - - -class VlanSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Vlan` objects. - """ - - class Meta: - model = machines.Vlan - fields = ( - "vlan_id", - "name", - "comment", - "arp_protect", - "dhcp_snooping", - "dhcpv6_snooping", - "igmp", - "mld", - "api_url", - ) - - -class NasSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Nas` objects. - """ - - class Meta: - model = machines.Nas - fields = ( - "name", - "nas_type", - "machine_type", - "port_access_mode", - "autocapture_mac", - "api_url", - ) - - -class SOASerializer(NamespacedHMSerializer): - """Serialize `machines.models.SOA` objects. - """ - - class Meta: - model = machines.SOA - fields = ("name", "mail", "refresh", "retry", "expire", "ttl", "api_url") - - -class ExtensionSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Extension` objects. - """ - - class Meta: - model = machines.Extension - fields = ("name", "need_infra", "origin", "origin_v6", "soa", "api_url") - - -class MxSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Mx` objects. - """ - - class Meta: - model = machines.Mx - fields = ("zone", "priority", "name", "api_url") - - -class DNameSerializer(NamespacedHMSerializer): - """Serialize `machines.models.DName` objects. - """ - - class Meta: - model = machines.DName - fields = ("zone", "alias", "api_url") - - -class NsSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Ns` objects. - """ - - class Meta: - model = machines.Ns - fields = ("zone", "ns", "api_url") - - -class TxtSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Txt` objects. - """ - - class Meta: - model = machines.Txt - fields = ("zone", "field1", "field2", "api_url") - - -class SrvSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Srv` objects. - """ - - class Meta: - model = machines.Srv - fields = ( - "service", - "protocole", - "extension", - "ttl", - "priority", - "weight", - "port", - "target", - "api_url", - ) - - -class SshFpSerializer(NamespacedHMSerializer): - """Serialize `machines.models.SSHFP` objects. - """ - - class Meta: - model = machines.SshFp - field = ("machine", "pub_key_entry", "algo", "comment", "api_url") - - -class InterfaceSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Interface` objects. - """ - - mac_address = serializers.CharField() - active = serializers.BooleanField(source="is_active") - - class Meta: - model = machines.Interface - fields = ( - "ipv4", - "mac_address", - "machine", - "machine_type", - "details", - "port_lists", - "active", - "api_url", - ) - - -class Ipv6ListSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Ipv6List` objects. - """ - - class Meta: - model = machines.Ipv6List - fields = ("ipv6", "interface", "slaac_ip", "api_url") - - -class DomainSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Domain` objects. - """ - - class Meta: - model = machines.Domain - fields = ("interface_parent", "name", "extension", "cname", "api_url", "ttl") - - -class IpListSerializer(NamespacedHMSerializer): - """Serialize `machines.models.IpList` objects. - """ - - class Meta: - model = machines.IpList - fields = ("ipv4", "ip_type", "need_infra", "api_url") - - -class ServiceSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Service` objects. - """ - - class Meta: - model = machines.Service - fields = ( - "service_type", - "min_time_regen", - "regular_time_regen", - "servers", - "api_url", - ) - - -class ServiceLinkSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Service_link` objects. - """ - - class Meta: - model = machines.Service_link - fields = ( - "service", - "server", - "last_regen", - "asked_regen", - "need_regen", - "api_url", - ) - extra_kwargs = {"api_url": {"view_name": "servicelink-detail"}} - - -class OuverturePortListSerializer(NamespacedHMSerializer): - """Serialize `machines.models.OuverturePortList` objects. - """ - - tcp_ports_in = NamespacedHRField( - view_name="ouvertureport-detail", many=True, read_only=True - ) - udp_ports_in = NamespacedHRField( - view_name="ouvertureport-detail", many=True, read_only=True - ) - tcp_ports_out = NamespacedHRField( - view_name="ouvertureport-detail", many=True, read_only=True - ) - udp_ports_out = NamespacedHRField( - view_name="ouvertureport-detail", many=True, read_only=True - ) - - class Meta: - model = machines.OuverturePortList - fields = ( - "name", - "tcp_ports_in", - "udp_ports_in", - "tcp_ports_out", - "udp_ports_out", - "api_url", - ) - - -class OuverturePortSerializer(NamespacedHMSerializer): - """Serialize `machines.models.OuverturePort` objects. - """ - - class Meta: - model = machines.OuverturePort - fields = ("begin", "end", "port_list", "protocole", "io", "api_url") - - -class RoleSerializer(NamespacedHMSerializer): - """Serialize `machines.models.OuverturePort` objects. - """ - - servers = InterfaceSerializer(read_only=True, many=True) - - class Meta: - model = machines.Role - fields = ("role_type", "servers", "api_url") - - -# PREFERENCES - - -class OptionalUserSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.OptionalUser` objects. - """ - - tel_mandatory = serializers.BooleanField(source="is_tel_mandatory") - shell_default = serializers.StringRelatedField() - - class Meta: - model = preferences.OptionalUser - fields = ( - "tel_mandatory", - "gpg_fingerprint", - "all_can_create_club", - "self_adhesion", - "shell_default", - "self_change_shell", - "local_email_accounts_enabled", - "local_email_domain", - "max_email_address", - ) - - -class OptionalMachineSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.OptionalMachine` objects. - """ - - class Meta: - model = preferences.OptionalMachine - fields = ( - "password_machine", - "max_lambdauser_interfaces", - "max_lambdauser_aliases", - "ipv6_mode", - "create_machine", - "ipv6", - "default_dns_ttl" - ) - - -class OptionalTopologieSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.OptionalTopologie` objects. - """ - - switchs_management_interface_ip = serializers.CharField() - - class Meta: - model = preferences.OptionalTopologie - fields = ( - "switchs_ip_type", - "switchs_web_management", - "switchs_web_management_ssl", - "switchs_rest_management", - "switchs_management_utils", - "switchs_management_interface_ip", - "provision_switchs_enabled", - "switchs_provision", - "switchs_management_sftp_creds", - ) - - -class RadiusOptionSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.RadiusOption` objects - """ - - class Meta: - model = preferences.RadiusOption - fields = ( - "radius_general_policy", - "unknown_machine", - "unknown_machine_vlan", - "unknown_port", - "unknown_port_vlan", - "unknown_room", - "unknown_room_vlan", - "non_member", - "non_member_vlan", - "banned", - "banned_vlan", - "vlan_decision_ok", - ) - - -class GeneralOptionSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.GeneralOption` objects. - """ - - class Meta: - model = preferences.GeneralOption - fields = ( - "general_message_fr", - "general_message_en", - "search_display_page", - "pagination_number", - "pagination_large_number", - "req_expire_hrs", - "site_name", - "main_site_url", - "email_from", - "GTU_sum_up", - "GTU", - ) - - -class HomeServiceSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.Service` objects. - """ - - class Meta: - model = preferences.Service - fields = ("name", "url", "description", "image", "api_url") - extra_kwargs = {"api_url": {"view_name": "homeservice-detail"}} - - -class AssoOptionSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.AssoOption` objects. - """ - - class Meta: - model = preferences.AssoOption - fields = ( - "name", - "siret", - "adresse1", - "adresse2", - "contact", - "telephone", - "pseudo", - "utilisateur_asso", - "description", - ) - - -class HomeOptionSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.HomeOption` objects. - """ - - class Meta: - model = preferences.HomeOption - fields = ("facebook_url", "twitter_url", "twitter_account_name") - - -class MailMessageOptionSerializer(NamespacedHMSerializer): - """Serialize `preferences.models.MailMessageOption` objects. - """ - - class Meta: - model = preferences.MailMessageOption - fields = ("welcome_mail_fr", "welcome_mail_en") - - -# TOPOLOGIE - - -class StackSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Stack` objects - """ - - class Meta: - model = topologie.Stack - fields = ( - "name", - "stack_id", - "details", - "member_id_min", - "member_id_max", - "api_url", - ) - - -class AccessPointSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.AccessPoint` objects - """ - - class Meta: - model = topologie.AccessPoint - fields = ("user", "name", "active", "location", "api_url") - - -class SwitchSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Switch` objects - """ - - port_amount = serializers.IntegerField(source="number") - - class Meta: - model = topologie.Switch - fields = ( - "user", - "name", - "active", - "port_amount", - "stack", - "stack_member_id", - "model", - "switchbay", - "api_url", - ) - - -class ServerSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Server` objects - """ - - class Meta: - model = topologie.Server - fields = ("user", "name", "active", "api_url") - - -class ModelSwitchSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.ModelSwitch` objects - """ - - class Meta: - model = topologie.ModelSwitch - fields = ("reference", "constructor", "api_url") - - -class ConstructorSwitchSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.ConstructorSwitch` objects - """ - - class Meta: - model = topologie.ConstructorSwitch - fields = ("name", "api_url") - - -class SwitchBaySerializer(NamespacedHMSerializer): - """Serialize `topologie.models.SwitchBay` objects - """ - - class Meta: - model = topologie.SwitchBay - fields = ("name", "building", "info", "api_url") - - -class BuildingSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Building` objects - """ - - class Meta: - model = topologie.Building - fields = ("name", "api_url") - - -class SwitchPortSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Port` objects - """ - - get_port_profile = NamespacedHIField(view_name="portprofile-detail", read_only=True) - - class Meta: - model = topologie.Port - fields = ( - "switch", - "port", - "room", - "machine_interface", - "related", - "custom_profile", - "state", - "get_port_profile", - "details", - "api_url", - ) - extra_kwargs = { - "related": {"view_name": "switchport-detail"}, - "api_url": {"view_name": "switchport-detail"}, - } - - -class PortProfileSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Room` objects - """ - - class Meta: - model = topologie.PortProfile - fields = ( - "name", - "profil_default", - "vlan_untagged", - "vlan_tagged", - "radius_type", - "radius_mode", - "speed", - "mac_limit", - "flow_control", - "dhcp_snooping", - "dhcpv6_snooping", - "dhcpv6_snooping", - "arp_protect", - "ra_guard", - "loop_protect", - "api_url", - ) - - -class RoomSerializer(NamespacedHMSerializer): - """Serialize `topologie.models.Room` objects - """ - - class Meta: - model = topologie.Room - fields = ("name", "details", "api_url") - - -class PortProfileSerializer(NamespacedHMSerializer): - vlan_untagged = VlanSerializer(read_only=True) - - class Meta: - model = topologie.PortProfile - fields = ( - "name", - "profil_default", - "vlan_untagged", - "vlan_tagged", - "radius_type", - "radius_mode", - "speed", - "mac_limit", - "flow_control", - "dhcp_snooping", - "dhcpv6_snooping", - "arp_protect", - "ra_guard", - "loop_protect", - "vlan_untagged", - "vlan_tagged", - ) - - -# USERS - - -class UserSerializer(NamespacedHMSerializer): - """Serialize `users.models.User` objects. - """ - - access = serializers.BooleanField(source="has_access") - uid = serializers.IntegerField(source="uid_number") - - class Meta: - model = users.User - fields = ( - "surname", - "pseudo", - "email", - "local_email_redirect", - "local_email_enabled", - "school", - "shell", - "comment", - "state", - "registered", - "telephone", - "solde", - "access", - "end_access", - "uid", - "class_type", - "api_url", - ) - extra_kwargs = {"shell": {"view_name": "shell-detail"}} - - -class ClubSerializer(NamespacedHMSerializer): - """Serialize `users.models.Club` objects. - """ - - name = serializers.CharField(source="surname") - access = serializers.BooleanField(source="has_access") - uid = serializers.IntegerField(source="uid_number") - - class Meta: - model = users.Club - fields = ( - "name", - "pseudo", - "email", - "local_email_redirect", - "local_email_enabled", - "school", - "shell", - "comment", - "state", - "registered", - "telephone", - "solde", - "room", - "access", - "end_access", - "administrators", - "members", - "mailing", - "uid", - "api_url", - ) - extra_kwargs = {"shell": {"view_name": "shell-detail"}} - - -class AdherentSerializer(NamespacedHMSerializer): - """Serialize `users.models.Adherent` objects. - """ - - access = serializers.BooleanField(source="has_access") - uid = serializers.IntegerField(source="uid_number") - - class Meta: - model = users.Adherent - fields = ( - "name", - "surname", - "pseudo", - "email", - "local_email_redirect", - "local_email_enabled", - "school", - "shell", - "comment", - "state", - "registered", - "telephone", - "room", - "solde", - "access", - "end_access", - "uid", - "api_url", - "gid", - ) - extra_kwargs = {"shell": {"view_name": "shell-detail"}} - - -class BasicUserSerializer(NamespacedHMSerializer): - """Serialize 'users.models.User' minimal infos""" - - uid = serializers.IntegerField(source="uid_number") - gid = serializers.IntegerField(source="gid_number") - - class Meta: - model = users.User - fields = ("pseudo", "uid", "gid") - - -class ServiceUserSerializer(NamespacedHMSerializer): - """Serialize `users.models.ServiceUser` objects. - """ - - class Meta: - model = users.ServiceUser - fields = ("pseudo", "access_group", "comment", "api_url") - - -class SchoolSerializer(NamespacedHMSerializer): - """Serialize `users.models.School` objects. - """ - - class Meta: - model = users.School - fields = ("name", "api_url") - - -class ListRightSerializer(NamespacedHMSerializer): - """Serialize `users.models.ListRight` objects. - """ - - class Meta: - model = users.ListRight - fields = ("unix_name", "gid", "critical", "details", "api_url") - - -class ShellSerializer(NamespacedHMSerializer): - """Serialize `users.models.ListShell` objects. - """ - - class Meta: - model = users.ListShell - fields = ("shell", "api_url") - extra_kwargs = {"api_url": {"view_name": "shell-detail"}} - - -class BanSerializer(NamespacedHMSerializer): - """Serialize `users.models.Ban` objects. - """ - - active = serializers.BooleanField(source="is_active") - - class Meta: - model = users.Ban - fields = ( - "user", - "raison", - "date_start", - "date_end", - "state", - "active", - "api_url", - ) - - -class WhitelistSerializer(NamespacedHMSerializer): - """Serialize `users.models.Whitelist` objects. - """ - - active = serializers.BooleanField(source="is_active") - - class Meta: - model = users.Whitelist - fields = ("user", "raison", "date_start", "date_end", "active", "api_url") - - -class EMailAddressSerializer(NamespacedHMSerializer): - """Serialize `users.models.EMailAddress` objects. - """ - - user = serializers.CharField(source="user.pseudo", read_only=True) - - class Meta: - model = users.EMailAddress - fields = ("user", "local_part", "complete_email_address", "api_url") - - -# SERVICE REGEN - - -class ServiceRegenSerializer(NamespacedHMSerializer): - """Serialize the data about the services to regen. - """ - - hostname = serializers.CharField(source="server.domain.name", read_only=True) - service_name = serializers.CharField(source="service.service_type", read_only=True) - need_regen = serializers.BooleanField() - - class Meta: - model = machines.Service_link - fields = ("hostname", "service_name", "need_regen", "api_url") - extra_kwargs = {"api_url": {"view_name": "serviceregen-detail"}} - - -# Switches et ports - - -class InterfaceVlanSerializer(NamespacedHMSerializer): - domain = serializers.CharField(read_only=True) - ipv4 = serializers.CharField(read_only=True) - ipv6 = Ipv6ListSerializer(read_only=True, many=True) - vlan_id = serializers.IntegerField( - source="machine_type.ip_type.vlan.vlan_id", read_only=True - ) - - class Meta: - model = machines.Interface - fields = ("ipv4", "ipv6", "domain", "vlan_id") - - -class InterfaceRoleSerializer(NamespacedHMSerializer): - interface = InterfaceVlanSerializer( - source="machine.interface_set", read_only=True, many=True - ) - - class Meta: - model = machines.Interface - fields = ("interface",) - - -class RoleSerializer(NamespacedHMSerializer): - """Serialize `machines.models.OuverturePort` objects. - """ - - servers = InterfaceRoleSerializer(read_only=True, many=True) - - class Meta: - model = machines.Role - fields = ("role_type", "servers", "specific_role") - - -class VlanPortSerializer(NamespacedHMSerializer): - class Meta: - model = machines.Vlan - fields = ("vlan_id", "name") - - -class ProfilSerializer(NamespacedHMSerializer): - vlan_untagged = VlanSerializer(read_only=True) - vlan_tagged = VlanPortSerializer(read_only=True, many=True) - - class Meta: - model = topologie.PortProfile - fields = ( - "name", - "profil_default", - "vlan_untagged", - "vlan_tagged", - "radius_type", - "radius_mode", - "speed", - "mac_limit", - "flow_control", - "dhcp_snooping", - "dhcpv6_snooping", - "arp_protect", - "ra_guard", - "loop_protect", - "vlan_untagged", - "vlan_tagged", - ) - - -class ModelSwitchSerializer(NamespacedHMSerializer): - constructor = serializers.CharField(read_only=True) - - class Meta: - model = topologie.ModelSwitch - fields = ("reference", "firmware", "constructor") - - -class SwitchBaySerializer(NamespacedHMSerializer): - class Meta: - model = topologie.SwitchBay - fields = ("name",) - - -class PortsSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Ipv6List` objects. - """ - - get_port_profile = ProfilSerializer(read_only=True) - - class Meta: - model = topologie.Port - fields = ("state", "port", "pretty_name", "get_port_profile") - - -class SwitchPortSerializer(serializers.ModelSerializer): - """Serialize the data about the switches""" - - ports = PortsSerializer(many=True, read_only=True) - model = ModelSwitchSerializer(read_only=True) - switchbay = SwitchBaySerializer(read_only=True) - - class Meta: - model = topologie.Switch - fields = ( - "short_name", - "model", - "switchbay", - "ports", - "ipv4", - "ipv6", - "interfaces_subnet", - "interfaces6_subnet", - "automatic_provision", - "rest_enabled", - "web_management_enabled", - "get_radius_key_value", - "get_management_cred_value", - "get_radius_servers", - "list_modules", - ) - - -# LOCAL EMAILS - - -class LocalEmailUsersSerializer(NamespacedHMSerializer): - email_address = EMailAddressSerializer(read_only=True, many=True) - - class Meta: - model = users.User - fields = ( - "local_email_enabled", - "local_email_redirect", - "email_address", - "email", - ) - - -# Firewall - - -class FirewallPortListSerializer(serializers.ModelSerializer): - class Meta: - model = machines.OuverturePort - fields = ("begin", "end", "protocole", "io", "show_port") - - -class FirewallOuverturePortListSerializer(serializers.ModelSerializer): - tcp_ports_in = FirewallPortListSerializer(many=True, read_only=True) - udp_ports_in = FirewallPortListSerializer(many=True, read_only=True) - tcp_ports_out = FirewallPortListSerializer(many=True, read_only=True) - udp_ports_out = FirewallPortListSerializer(many=True, read_only=True) - - class Meta: - model = machines.OuverturePortList - fields = ("tcp_ports_in", "udp_ports_in", "tcp_ports_out", "udp_ports_out") - - -class SubnetPortsOpenSerializer(serializers.ModelSerializer): - ouverture_ports = FirewallOuverturePortListSerializer(read_only=True) - - class Meta: - model = machines.IpType - fields = ( - "name", - "domaine_ip_start", - "domaine_ip_stop", - "complete_prefixv6", - "ouverture_ports", - ) - - -class InterfacePortsOpenSerializer(serializers.ModelSerializer): - port_lists = FirewallOuverturePortListSerializer(read_only=True, many=True) - ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) - ipv6 = Ipv6ListSerializer(many=True, read_only=True) - - class Meta: - model = machines.Interface - fields = ("port_lists", "ipv4", "ipv6") - - -# DHCP - - -class HostMacIpSerializer(serializers.ModelSerializer): - """Serialize the data about the hostname-ipv4-mac address association - to build the DHCP lease files. - """ - - hostname = serializers.CharField(source="domain.name", read_only=True) - extension = serializers.CharField(source="domain.extension.name", read_only=True) - mac_address = serializers.CharField(read_only=True) - ip_type = serializers.CharField(source="machine_type.ip_type", read_only=True) - ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) - - class Meta: - model = machines.Interface - fields = ("hostname", "extension", "mac_address", "ipv4", "ip_type") - - -# DNS - - -class SOARecordSerializer(SOASerializer): - """Serialize `machines.models.SOA` objects with the data needed to - generate a SOA DNS record. - """ - - class Meta: - model = machines.SOA - fields = ("name", "mail", "refresh", "retry", "expire", "ttl") - - -class OriginV4RecordSerializer(IpListSerializer): - """Serialize `machines.models.IpList` objects with the data needed to - generate an IPv4 Origin DNS record. - """ - - class Meta(IpListSerializer.Meta): - fields = ("ipv4",) - - -class NSRecordSerializer(NsSerializer): - """Serialize `machines.models.Ns` objects with the data needed to - generate a NS DNS record. - """ - - target = serializers.CharField(source="ns", read_only=True) - - class Meta(NsSerializer.Meta): - fields = ("target", "ttl") - - -class MXRecordSerializer(MxSerializer): - """Serialize `machines.models.Mx` objects with the data needed to - generate a MX DNS record. - """ - - target = serializers.CharField(source="name", read_only=True) - - class Meta(MxSerializer.Meta): - fields = ("target", "priority", "ttl") - - -class TXTRecordSerializer(TxtSerializer): - """Serialize `machines.models.Txt` objects with the data needed to - generate a TXT DNS record. - """ - - class Meta(TxtSerializer.Meta): - fields = ("field1", "field2", "ttl") - - -class SRVRecordSerializer(SrvSerializer): - """Serialize `machines.models.Srv` objects with the data needed to - generate a SRV DNS record. - """ - - target = serializers.CharField(source="target.name", read_only=True) - - class Meta(SrvSerializer.Meta): - fields = ("service", "protocole", "ttl", "priority", "weight", "port", "target") - - -class SSHFPRecordSerializer(SshFpSerializer): - """Serialize `machines.models.SshFp` objects with the data needed to - generate a SSHFP DNS record. - """ - - class Meta(SshFpSerializer.Meta): - fields = ("algo_id", "hash") - - -class SSHFPInterfaceSerializer(serializers.ModelSerializer): - """Serialize `machines.models.Domain` objects with the data needed to - generate a CNAME DNS record. - """ - - hostname = serializers.CharField(source="domain.name", read_only=True) - sshfp = SSHFPRecordSerializer(source="machine.sshfp_set", many=True, read_only=True) - - class Meta: - model = machines.Interface - fields = ("hostname", "sshfp") - - -class ARecordSerializer(serializers.ModelSerializer): - """Serialize `machines.models.Interface` objects with the data needed to - generate a A DNS record. - """ - - hostname = serializers.CharField(source="domain.name", read_only=True) - ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) - ttl = serializers.IntegerField(source="domain.ttl", read_only=True) - - class Meta: - model = machines.Interface - fields = ("hostname", "ipv4", "ttl") - - -class AAAARecordSerializer(serializers.ModelSerializer): - """Serialize `machines.models.Interface` objects with the data needed to - generate a AAAA DNS record. - """ - - hostname = serializers.CharField(source="domain.name", read_only=True) - ipv6 = Ipv6ListSerializer(many=True, read_only=True) - ttl = serializers.IntegerField(source="domain.ttl", read_only=True) - - class Meta: - model = machines.Interface - fields = ("hostname", "ipv6", "ttl") - - -class CNAMERecordSerializer(serializers.ModelSerializer): - """Serialize `machines.models.Domain` objects with the data needed to - generate a CNAME DNS record. - """ - - alias = serializers.CharField(source="cname", read_only=True) - hostname = serializers.CharField(source="name", read_only=True) - - class Meta: - model = machines.Domain - fields = ("alias", "hostname", "ttl") - - -class DNAMERecordSerializer(serializers.ModelSerializer): - """Serialize `machines.models.Domain` objects with the data needed to - generate a DNAME DNS record. - """ - - alias = serializers.CharField(read_only=True) - zone = serializers.CharField(read_only=True) - - class Meta: - model = machines.DName - fields = ("alias", "zone", "ttl") - - -class DNSZonesSerializer(serializers.ModelSerializer): - """Serialize the data about DNS Zones. - """ - - soa = SOARecordSerializer() - ns_records = NSRecordSerializer(many=True, source="ns_set") - originv4 = OriginV4RecordSerializer(source="origin") - originv6 = serializers.CharField(source="origin_v6") - mx_records = MXRecordSerializer(many=True, source="mx_set") - txt_records = TXTRecordSerializer(many=True, source="txt_set") - srv_records = SRVRecordSerializer(many=True, source="srv_set") - a_records = ARecordSerializer(many=True, source="get_associated_a_records") - aaaa_records = AAAARecordSerializer(many=True, source="get_associated_aaaa_records") - cname_records = CNAMERecordSerializer( - many=True, source="get_associated_cname_records" - ) - dname_records = DNAMERecordSerializer( - many=True, source="get_associated_dname_records" - ) - sshfp_records = SSHFPInterfaceSerializer( - many=True, source="get_associated_sshfp_records" - ) - - class Meta: - model = machines.Extension - fields = ( - "name", - "soa", - "ns_records", - "originv4", - "originv6", - "mx_records", - "txt_records", - "srv_records", - "a_records", - "aaaa_records", - "cname_records", - "dname_records", - "sshfp_records", - ) - - -# REMINDER - class ReminderUsersSerializer(UserSerializer): """Serialize the data about a mailing member. @@ -1382,57 +84,3 @@ class ReminderSerializer(serializers.ModelSerializer): fields = ("days", "message", "users_to_remind") -class DNSReverseZonesSerializer(serializers.ModelSerializer): - """Serialize the data about DNS Zones. - """ - - soa = SOARecordSerializer(source="extension.soa") - extension = serializers.CharField(source="extension.name", read_only=True) - cidrs = serializers.ListField( - child=serializers.CharField(), source="ip_set_cidrs_as_str", read_only=True - ) - ns_records = NSRecordSerializer(many=True, source="extension.ns_set") - mx_records = MXRecordSerializer(many=True, source="extension.mx_set") - txt_records = TXTRecordSerializer(many=True, source="extension.txt_set") - ptr_records = ARecordSerializer(many=True, source="get_associated_ptr_records") - ptr_v6_records = AAAARecordSerializer( - many=True, source="get_associated_ptr_v6_records" - ) - - class Meta: - model = machines.IpType - fields = ( - "name", - "extension", - "soa", - "ns_records", - "mx_records", - "txt_records", - "ptr_records", - "ptr_v6_records", - "cidrs", - "prefix_v6", - "prefix_v6_length", - ) - - -# MAILING - - -class MailingMemberSerializer(UserSerializer): - """Serialize the data about a mailing member. - """ - - class Meta(UserSerializer.Meta): - fields = ("name", "pseudo", "get_mail") - - -class MailingSerializer(ClubSerializer): - """Serialize the data about a mailing. - """ - - members = MailingMemberSerializer(many=True) - admins = MailingMemberSerializer(source="administrators", many=True) - - class Meta(ClubSerializer.Meta): - fields = ("name", "members", "admins") diff --git a/api/urls.py b/api/urls.py index 6c2bd4c2..50681770 100644 --- a/api/urls.py +++ b/api/urls.py @@ -31,111 +31,34 @@ from django.conf.urls import url, include from . import views from .routers import AllViewsRouter +from cotisations.api.urls import urls_viewset as urls_viewset_cotisations +from cotisations.api.urls import urls_view as urls_view_cotisations +from machines.api.urls import urls_viewset as urls_viewset_machines +from machines.api.urls import urls_view as urls_view_machines +from preferences.api.urls import urls_viewset as urls_viewset_preferences +from preferences.api.urls import urls_view as urls_view_preferences +from topologie.api.urls import urls_viewset as urls_viewset_topologie +from topologie.api.urls import urls_view as urls_view_topologie +from users.api.urls import urls_viewset as urls_viewset_users +from users.api.urls import urls_view as urls_view_users + +urls_viewset = urls_viewset_cotisations + urls_viewset_machines + urls_viewset_preferences + urls_viewset_topologie + urls_viewset_users +urls_view = urls_view_cotisations + urls_view_machines + urls_view_preferences + urls_view_topologie + urls_view_users router = AllViewsRouter() -# COTISATIONS -router.register_viewset(r"cotisations/facture", views.FactureViewSet) -router.register_viewset(r"cotisations/vente", views.VenteViewSet) -router.register_viewset(r"cotisations/article", views.ArticleViewSet) -router.register_viewset(r"cotisations/banque", views.BanqueViewSet) -router.register_viewset(r"cotisations/paiement", views.PaiementViewSet) -router.register_viewset(r"cotisations/cotisation", views.CotisationViewSet) -# MACHINES -router.register_viewset(r"machines/machine", views.MachineViewSet) -router.register_viewset(r"machines/machinetype", views.MachineTypeViewSet) -router.register_viewset(r"machines/iptype", views.IpTypeViewSet) -router.register_viewset(r"machines/vlan", views.VlanViewSet) -router.register_viewset(r"machines/nas", views.NasViewSet) -router.register_viewset(r"machines/soa", views.SOAViewSet) -router.register_viewset(r"machines/extension", views.ExtensionViewSet) -router.register_viewset(r"machines/mx", views.MxViewSet) -router.register_viewset(r"machines/ns", views.NsViewSet) -router.register_viewset(r"machines/txt", views.TxtViewSet) -router.register_viewset(r"machines/dname", views.DNameViewSet) -router.register_viewset(r"machines/srv", views.SrvViewSet) -router.register_viewset(r"machines/sshfp", views.SshFpViewSet) -router.register_viewset(r"machines/interface", views.InterfaceViewSet) -router.register_viewset(r"machines/ipv6list", views.Ipv6ListViewSet) -router.register_viewset(r"machines/domain", views.DomainViewSet) -router.register_viewset(r"machines/iplist", views.IpListViewSet) -router.register_viewset(r"machines/service", views.ServiceViewSet) -router.register_viewset( - r"machines/servicelink", views.ServiceLinkViewSet, base_name="servicelink" -) -router.register_viewset(r"machines/ouvertureportlist", views.OuverturePortListViewSet) -router.register_viewset(r"machines/ouvertureport", views.OuverturePortViewSet) -router.register_viewset(r"machines/role", views.RoleViewSet) -# PREFERENCES -router.register_view(r"preferences/optionaluser", views.OptionalUserView), -router.register_view(r"preferences/optionalmachine", views.OptionalMachineView), -router.register_view(r"preferences/optionaltopologie", views.OptionalTopologieView), -router.register_view(r"preferences/radiusoption", views.RadiusOptionView), -router.register_view(r"preferences/generaloption", views.GeneralOptionView), -router.register_viewset( - r"preferences/service", views.HomeServiceViewSet, base_name="homeservice" -), -router.register_view(r"preferences/assooption", views.AssoOptionView), -router.register_view(r"preferences/homeoption", views.HomeOptionView), -router.register_view(r"preferences/mailmessageoption", views.MailMessageOptionView), -# TOPOLOGIE -router.register_viewset(r"topologie/stack", views.StackViewSet) -router.register_viewset(r"topologie/acesspoint", views.AccessPointViewSet) -router.register_viewset(r"topologie/switch", views.SwitchViewSet) -router.register_viewset(r"topologie/server", views.ServerViewSet) -router.register_viewset(r"topologie/modelswitch", views.ModelSwitchViewSet) -router.register_viewset(r"topologie/constructorswitch", views.ConstructorSwitchViewSet) -router.register_viewset(r"topologie/switchbay", views.SwitchBayViewSet) -router.register_viewset(r"topologie/building", views.BuildingViewSet) -router.register_viewset( - r"topologie/switchport", views.SwitchPortViewSet, base_name="switchport" -) -router.register_viewset( - r"topologie/portprofile", views.PortProfileViewSet, base_name="portprofile" -) -router.register_viewset(r"topologie/room", views.RoomViewSet) -router.register(r"topologie/portprofile", views.PortProfileViewSet) -# USERS -router.register_viewset(r"users/user", views.UserViewSet, base_name="user") -router.register_viewset( - r"users/homecreation", views.HomeCreationViewSet, base_name="homecreation" -) -router.register_viewset( - r"users/normaluser", views.NormalUserViewSet, base_name="normaluser" -) -router.register_viewset( - r"users/criticaluser", views.CriticalUserViewSet, base_name="criticaluser" -) -router.register_viewset(r"users/club", views.ClubViewSet) -router.register_viewset(r"users/adherent", views.AdherentViewSet) -router.register_viewset(r"users/serviceuser", views.ServiceUserViewSet) -router.register_viewset(r"users/school", views.SchoolViewSet) -router.register_viewset(r"users/listright", views.ListRightViewSet) -router.register_viewset(r"users/shell", views.ShellViewSet, base_name="shell") -router.register_viewset(r"users/ban", views.BanViewSet) -router.register_viewset(r"users/whitelist", views.WhitelistViewSet) -router.register_viewset(r"users/emailaddress", views.EMailAddressViewSet) -# SERVICE REGEN -router.register_viewset( - r"services/regen", views.ServiceRegenViewSet, base_name="serviceregen" -) -# DHCP -router.register_view(r"dhcp/hostmacip", views.HostMacIpView), -# LOCAL EMAILS -router.register_view(r"localemail/users", views.LocalEmailUsersView), -# Firewall -router.register_view(r"firewall/subnet-ports", views.SubnetPortsOpenView), -router.register_view(r"firewall/interface-ports", views.InterfacePortsOpenView), -# Switches config -router.register_view(r"switchs/ports-config", views.SwitchPortView), -router.register_view(r"switchs/role", views.RoleView), + + +for _url, viewset, name in urls_viewset: + if name == None: + router.register_viewset(_url, viewset) + else: + router.register_viewset(_url, viewset, basename=name) + +for _url, view in urls_view: + router.register_view(_url, view) + # Reminder router.register_view(r"reminder/get-users", views.ReminderView), -# DNS -router.register_view(r"dns/zones", views.DNSZonesView), -router.register_view(r"dns/reverse-zones", views.DNSReverseZonesView), -# MAILING -router.register_view(r"mailing/standard", views.StandardMailingView), -router.register_view(r"mailing/club", views.ClubMailingView), # TOKEN AUTHENTICATION router.register_view(r"token-auth", views.ObtainExpiringAuthToken) diff --git a/api/views.py b/api/views.py index 4077eeeb..d17622fc 100644 --- a/api/views.py +++ b/api/views.py @@ -36,7 +36,7 @@ from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.response import Response -import cotisations.models as cotisations +# import cotisations.models as cotisations import machines.models as machines import preferences.models as preferences import topologie.models as topologie @@ -47,611 +47,6 @@ from .pagination import PageSizedPagination from .permissions import ACLPermission -# COTISATIONS - - -class FactureViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Facture` objects. - """ - - queryset = cotisations.Facture.objects.all() - serializer_class = serializers.FactureSerializer - - -class FactureViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Facture` objects. - """ - - queryset = cotisations.BaseInvoice.objects.all() - serializer_class = serializers.BaseInvoiceSerializer - - -class VenteViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Vente` objects. - """ - - queryset = cotisations.Vente.objects.all() - serializer_class = serializers.VenteSerializer - - -class ArticleViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Article` objects. - """ - - queryset = cotisations.Article.objects.all() - serializer_class = serializers.ArticleSerializer - - -class BanqueViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Banque` objects. - """ - - queryset = cotisations.Banque.objects.all() - serializer_class = serializers.BanqueSerializer - - -class PaiementViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Paiement` objects. - """ - - queryset = cotisations.Paiement.objects.all() - serializer_class = serializers.PaiementSerializer - - -class CotisationViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `cotisations.models.Cotisation` objects. - """ - - queryset = cotisations.Cotisation.objects.all() - serializer_class = serializers.CotisationSerializer - - -# MACHINES - - -class MachineViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Machine` objects. - """ - - queryset = machines.Machine.objects.all() - serializer_class = serializers.MachineSerializer - - -class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.MachineType` objects. - """ - - queryset = machines.MachineType.objects.all() - serializer_class = serializers.MachineTypeSerializer - - -class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.IpType` objects. - """ - - queryset = machines.IpType.objects.all() - serializer_class = serializers.IpTypeSerializer - - -class VlanViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Vlan` objects. - """ - - queryset = machines.Vlan.objects.all() - serializer_class = serializers.VlanSerializer - - -class NasViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Nas` objects. - """ - - queryset = machines.Nas.objects.all() - serializer_class = serializers.NasSerializer - - -class SOAViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.SOA` objects. - """ - - queryset = machines.SOA.objects.all() - serializer_class = serializers.SOASerializer - - -class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Extension` objects. - """ - - queryset = machines.Extension.objects.all() - serializer_class = serializers.ExtensionSerializer - - -class MxViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Mx` objects. - """ - - queryset = machines.Mx.objects.all() - serializer_class = serializers.MxSerializer - - -class NsViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Ns` objects. - """ - - queryset = machines.Ns.objects.all() - serializer_class = serializers.NsSerializer - - -class TxtViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Txt` objects. - """ - - queryset = machines.Txt.objects.all() - serializer_class = serializers.TxtSerializer - - -class DNameViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.DName` objects. - """ - - queryset = machines.DName.objects.all() - serializer_class = serializers.DNameSerializer - - -class SrvViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Srv` objects. - """ - - queryset = machines.Srv.objects.all() - serializer_class = serializers.SrvSerializer - - -class SshFpViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.SshFp` objects. - """ - - queryset = machines.SshFp.objects.all() - serializer_class = serializers.SshFpSerializer - - -class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Interface` objects. - """ - - queryset = machines.Interface.objects.all() - serializer_class = serializers.InterfaceSerializer - - -class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Ipv6List` objects. - """ - - queryset = machines.Ipv6List.objects.all() - serializer_class = serializers.Ipv6ListSerializer - - -class DomainViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Domain` objects. - """ - - queryset = machines.Domain.objects.all() - serializer_class = serializers.DomainSerializer - - -class IpListViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.IpList` objects. - """ - - queryset = machines.IpList.objects.all() - serializer_class = serializers.IpListSerializer - - -class ServiceViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Service` objects. - """ - - queryset = machines.Service.objects.all() - serializer_class = serializers.ServiceSerializer - - -class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Service_link` objects. - """ - - queryset = machines.Service_link.objects.all() - serializer_class = serializers.ServiceLinkSerializer - - -class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.OuverturePortList` - objects. - """ - - queryset = machines.OuverturePortList.objects.all() - serializer_class = serializers.OuverturePortListSerializer - - -class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.OuverturePort` objects. - """ - - queryset = machines.OuverturePort.objects.all() - serializer_class = serializers.OuverturePortSerializer - - -class RoleViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `machines.models.Machine` objects. - """ - - queryset = machines.Role.objects.all() - serializer_class = serializers.RoleSerializer - - -# PREFERENCES -# Those views differ a bit because there is only one object -# to display, so we don't bother with the listing part - - -class OptionalUserView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.OptionalUser.can_view_all]} - serializer_class = serializers.OptionalUserSerializer - - def get_object(self): - return preferences.OptionalUser.objects.first() - - -class OptionalMachineView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.OptionalMachine` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.OptionalMachine.can_view_all]} - serializer_class = serializers.OptionalMachineSerializer - - def get_object(self): - return preferences.OptionalMachine.objects.first() - - -class OptionalTopologieView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.OptionalTopologie` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]} - serializer_class = serializers.OptionalTopologieSerializer - - def get_object(self): - return preferences.OptionalTopologie.objects.first() - - -class RadiusOptionView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.OptionalTopologie` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.RadiusOption.can_view_all]} - serializer_class = serializers.RadiusOptionSerializer - - def get_object(self): - return preferences.RadiusOption.objects.first() - - -class GeneralOptionView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.GeneralOption` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.GeneralOption.can_view_all]} - serializer_class = serializers.GeneralOptionSerializer - - def get_object(self): - return preferences.GeneralOption.objects.first() - - -class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `preferences.models.Service` objects. - """ - - queryset = preferences.Service.objects.all() - serializer_class = serializers.HomeServiceSerializer - - -class AssoOptionView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.AssoOption` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.AssoOption.can_view_all]} - serializer_class = serializers.AssoOptionSerializer - - def get_object(self): - return preferences.AssoOption.objects.first() - - -class HomeOptionView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.HomeOption` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.HomeOption.can_view_all]} - serializer_class = serializers.HomeOptionSerializer - - def get_object(self): - return preferences.HomeOption.objects.first() - - -class MailMessageOptionView(generics.RetrieveAPIView): - """Exposes details of `preferences.models.MailMessageOption` settings. - """ - - permission_classes = (ACLPermission,) - perms_map = {"GET": [preferences.MailMessageOption.can_view_all]} - serializer_class = serializers.MailMessageOptionSerializer - - def get_object(self): - return preferences.MailMessageOption.objects.first() - - -# TOPOLOGIE - - -class StackViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Stack` objects. - """ - - queryset = topologie.Stack.objects.all() - serializer_class = serializers.StackSerializer - - -class AccessPointViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.AccessPoint` objects. - """ - - queryset = topologie.AccessPoint.objects.all() - serializer_class = serializers.AccessPointSerializer - - -class SwitchViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Switch` objects. - """ - - queryset = topologie.Switch.objects.all() - serializer_class = serializers.SwitchSerializer - - -class ServerViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Server` objects. - """ - - queryset = topologie.Server.objects.all() - serializer_class = serializers.ServerSerializer - - -class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.ModelSwitch` objects. - """ - - queryset = topologie.ModelSwitch.objects.all() - serializer_class = serializers.ModelSwitchSerializer - - -class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.ConstructorSwitch` - objects. - """ - - queryset = topologie.ConstructorSwitch.objects.all() - serializer_class = serializers.ConstructorSwitchSerializer - - -class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.SwitchBay` objects. - """ - - queryset = topologie.SwitchBay.objects.all() - serializer_class = serializers.SwitchBaySerializer - - -class BuildingViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Building` objects. - """ - - queryset = topologie.Building.objects.all() - serializer_class = serializers.BuildingSerializer - - -class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Port` objects. - """ - - queryset = topologie.Port.objects.all() - serializer_class = serializers.SwitchPortSerializer - - -class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.PortProfile` objects. - """ - - queryset = topologie.PortProfile.objects.all() - serializer_class = serializers.PortProfileSerializer - - -class RoomViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.Room` objects. - """ - - queryset = topologie.Room.objects.all() - serializer_class = serializers.RoomSerializer - - -class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `topologie.models.PortProfile` objects. - """ - - queryset = topologie.PortProfile.objects.all() - serializer_class = serializers.PortProfileSerializer - - -# USER - - -class UserViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.Users` objects. - """ - - queryset = users.User.objects.all() - serializer_class = serializers.UserSerializer - - -class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes infos of `users.models.Users` objects to create homes. - """ - - queryset = users.User.objects.exclude( - Q(state=users.User.STATE_DISABLED) - | Q(state=users.User.STATE_NOT_YET_ACTIVE) - | Q(state=users.User.STATE_FULL_ARCHIVE) - ) - serializer_class = serializers.BasicUserSerializer - - -class NormalUserViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes infos of `users.models.Users`without specific rights objects.""" - - queryset = users.User.objects.exclude(groups__listright__critical=True).distinct() - serializer_class = serializers.BasicUserSerializer - - -class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes infos of `users.models.Users`without specific rights objects.""" - - queryset = users.User.objects.filter(groups__listright__critical=True).distinct() - serializer_class = serializers.BasicUserSerializer - - -class ClubViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.Club` objects. - """ - - queryset = users.Club.objects.all() - serializer_class = serializers.ClubSerializer - - -class AdherentViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.Adherent` objects. - """ - - queryset = users.Adherent.objects.all() - serializer_class = serializers.AdherentSerializer - - -class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.ServiceUser` objects. - """ - - queryset = users.ServiceUser.objects.all() - serializer_class = serializers.ServiceUserSerializer - - -class SchoolViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.School` objects. - """ - - queryset = users.School.objects.all() - serializer_class = serializers.SchoolSerializer - - -class ListRightViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.ListRight` objects. - """ - - queryset = users.ListRight.objects.all() - serializer_class = serializers.ListRightSerializer - - -class ShellViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.ListShell` objects. - """ - - queryset = users.ListShell.objects.all() - serializer_class = serializers.ShellSerializer - - -class BanViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.Ban` objects. - """ - - queryset = users.Ban.objects.all() - serializer_class = serializers.BanSerializer - - -class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.Whitelist` objects. - """ - - queryset = users.Whitelist.objects.all() - serializer_class = serializers.WhitelistSerializer - - -class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet): - """Exposes list and details of `users.models.EMailAddress` objects. - """ - - serializer_class = serializers.EMailAddressSerializer - queryset = users.EMailAddress.objects.none() - - def get_queryset(self): - if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): - return users.EMailAddress.objects.filter(user__local_email_enabled=True) - else: - return users.EMailAddress.objects.none() - - -# SERVICE REGEN - - -class ServiceRegenViewSet(viewsets.ModelViewSet): - """Exposes list and details of the services to regen - """ - - serializer_class = serializers.ServiceRegenSerializer - - def get_queryset(self): - queryset = machines.Service_link.objects.select_related( - "server__domain" - ).select_related("service") - if "hostname" in self.request.GET: - hostname = self.request.GET["hostname"] - queryset = queryset.filter(server__domain__name__iexact=hostname) - return queryset - - -# Config des switches - - -class SwitchPortView(generics.ListAPIView): - """Output each port of a switch, to be serialized with - additionnal informations (profiles etc) - """ - - queryset = ( - topologie.Switch.objects.all() - .select_related("switchbay") - .select_related("model__constructor") - .prefetch_related("ports__custom_profile__vlan_tagged") - .prefetch_related("ports__custom_profile__vlan_untagged") - .prefetch_related("ports__machine_interface__domain__extension") - .prefetch_related("ports__room") - ) - - serializer_class = serializers.SwitchPortSerializer - - -# Rappel fin adhésion - class ReminderView(generics.ListAPIView): """Output for users to remind an end of their subscription. @@ -660,132 +55,6 @@ class ReminderView(generics.ListAPIView): queryset = preferences.Reminder.objects.all() serializer_class = serializers.ReminderSerializer - -class RoleView(generics.ListAPIView): - """Output of roles for each server - """ - - queryset = machines.Role.objects.all().prefetch_related("servers") - serializer_class = serializers.RoleSerializer - - -# LOCAL EMAILS - - -class LocalEmailUsersView(generics.ListAPIView): - """Exposes all the aliases of the users that activated the internal address - """ - - serializer_class = serializers.LocalEmailUsersSerializer - - def get_queryset(self): - if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): - return users.User.objects.filter(local_email_enabled=True) - else: - return users.User.objects.none() - - -# DHCP - - -class HostMacIpView(generics.ListAPIView): - """Exposes the associations between hostname, mac address and IPv4 in - order to build the DHCP lease files. - """ - - serializer_class = serializers.HostMacIpSerializer - - def get_queryset(self): - return all_active_interfaces() - - -# Firewall - - -class SubnetPortsOpenView(generics.ListAPIView): - queryset = machines.IpType.objects.all() - serializer_class = serializers.SubnetPortsOpenSerializer - - -class InterfacePortsOpenView(generics.ListAPIView): - queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct() - serializer_class = serializers.InterfacePortsOpenSerializer - - -# DNS - - -class DNSZonesView(generics.ListAPIView): - """Exposes the detailed information about each extension (hostnames, - IPs, DNS records, etc.) in order to build the DNS zone files. - """ - - queryset = ( - machines.Extension.objects.prefetch_related("soa") - .prefetch_related("ns_set") - .prefetch_related("ns_set__ns") - .prefetch_related("origin") - .prefetch_related("mx_set") - .prefetch_related("mx_set__name") - .prefetch_related("txt_set") - .prefetch_related("srv_set") - .prefetch_related("srv_set__target") - .all() - ) - serializer_class = serializers.DNSZonesSerializer - - -class DNSReverseZonesView(generics.ListAPIView): - """Exposes the detailed information about each extension (hostnames, - IPs, DNS records, etc.) in order to build the DNS zone files. - """ - - queryset = machines.IpType.objects.all() - serializer_class = serializers.DNSReverseZonesSerializer - - -# MAILING - - -class StandardMailingView(views.APIView): - """Exposes list and details of standard mailings (name and members) in - order to building the corresponding mailing lists. - """ - - pagination_class = PageSizedPagination - permission_classes = (ACLPermission,) - perms_map = {"GET": [users.User.can_view_all]} - - def get(self, request, format=None): - adherents_data = serializers.MailingMemberSerializer( - all_has_access(), many=True - ).data - - data = [{"name": "adherents", "members": adherents_data}] - groups = Group.objects.all() - for group in groups: - group_data = serializers.MailingMemberSerializer( - group.user_set.all(), many=True - ).data - data.append({"name": group.name, "members": group_data}) - - paginator = self.pagination_class() - paginator.paginate_queryset(data, request) - return paginator.get_paginated_response(data) - - -class ClubMailingView(generics.ListAPIView): - """Exposes list and details of club mailings (name, members and admins) in - order to build the corresponding mailing lists. - """ - - queryset = users.Club.objects.all() - serializer_class = serializers.MailingSerializer - - -# TOKEN AUTHENTICATION - - class ObtainExpiringAuthToken(ObtainAuthToken): """Exposes a view to obtain a authentication token. diff --git a/cotisations/api/__init__.py b/cotisations/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cotisations/api/serializers.py b/cotisations/api/serializers.py new file mode 100644 index 00000000..138f5b93 --- /dev/null +++ b/cotisations/api/serializers.py @@ -0,0 +1,105 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import serializers + +import cotisations.models as cotisations +from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer + + +class FactureSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Facture` objects. + """ + + class Meta: + model = cotisations.Facture + fields = ( + "user", + "paiement", + "banque", + "cheque", + "date", + "valid", + "control", + "prix_total", + "name", + "api_url", + ) + + +class BaseInvoiceSerializer(NamespacedHMSerializer): + class Meta: + model = cotisations.BaseInvoice + fields = "__all__" + + +class VenteSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Vente` objects. + """ + + class Meta: + model = cotisations.Vente + fields = ( + "facture", + "number", + "name", + "prix", + "duration", + "type_cotisation", + "prix_total", + "api_url", + ) + + +class ArticleSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Article` objects. + """ + + class Meta: + model = cotisations.Article + fields = ("name", "prix", "duration", "type_user", "type_cotisation", "api_url") + + +class BanqueSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Banque` objects. + """ + + class Meta: + model = cotisations.Banque + fields = ("name", "api_url") + + +class PaiementSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Paiement` objects. + """ + + class Meta: + model = cotisations.Paiement + fields = ("moyen", "api_url") + + +class CotisationSerializer(NamespacedHMSerializer): + """Serialize `cotisations.models.Cotisation` objects. + """ + + class Meta: + model = cotisations.Cotisation + fields = ("vente", "type_cotisation", "date_start", "date_end", "api_url") diff --git a/cotisations/api/urls.py b/cotisations/api/urls.py new file mode 100644 index 00000000..574c3cd2 --- /dev/null +++ b/cotisations/api/urls.py @@ -0,0 +1,35 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 . import views + +urls_viewset = [ + (r"cotisations/facture", views.FactureViewSet, None), + (r"cotisations/vente", views.VenteViewSet, None), + (r"cotisations/article", views.ArticleViewSet, None), + (r"cotisations/banque", views.BanqueViewSet, None), + (r"cotisations/paiement", views.PaiementViewSet, None), + (r"cotisations/cotisation", views.CotisationViewSet, None) +] + +urls_view = [ + # (r"reminder/get-users", views.ReminderView), +] \ No newline at end of file diff --git a/cotisations/api/views.py b/cotisations/api/views.py new file mode 100644 index 00000000..b7f5d13b --- /dev/null +++ b/cotisations/api/views.py @@ -0,0 +1,80 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import viewsets, generics + +from . import serializers +import cotisations.models as cotisations + +class FactureViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Facture` objects. + """ + + queryset = cotisations.Facture.objects.all() + serializer_class = serializers.FactureSerializer + + +class FactureViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Facture` objects. + """ + + queryset = cotisations.BaseInvoice.objects.all() + serializer_class = serializers.BaseInvoiceSerializer + + +class VenteViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Vente` objects. + """ + + queryset = cotisations.Vente.objects.all() + serializer_class = serializers.VenteSerializer + + +class ArticleViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Article` objects. + """ + + queryset = cotisations.Article.objects.all() + serializer_class = serializers.ArticleSerializer + + +class BanqueViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Banque` objects. + """ + + queryset = cotisations.Banque.objects.all() + serializer_class = serializers.BanqueSerializer + + +class PaiementViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Paiement` objects. + """ + + queryset = cotisations.Paiement.objects.all() + serializer_class = serializers.PaiementSerializer + + +class CotisationViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `cotisations.models.Cotisation` objects. + """ + + queryset = cotisations.Cotisation.objects.all() + serializer_class = serializers.CotisationSerializer diff --git a/machines/api/__init__.py b/machines/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/machines/api/serializers.py b/machines/api/serializers.py new file mode 100644 index 00000000..eeebb062 --- /dev/null +++ b/machines/api/serializers.py @@ -0,0 +1,592 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import serializers + +import machines.models as machines +from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer + + +class MachineSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Machine` objects. + """ + + class Meta: + model = machines.Machine + fields = ("user", "name", "active", "api_url") + + +class MachineTypeSerializer(NamespacedHMSerializer): + """Serialize `machines.models.MachineType` objects. + """ + + class Meta: + model = machines.MachineType + fields = ("name", "ip_type", "api_url") + + +class IpTypeSerializer(NamespacedHMSerializer): + """Serialize `machines.models.IpType` objects. + """ + + class Meta: + model = machines.IpType + fields = ( + "name", + "extension", + "need_infra", + "domaine_ip_start", + "domaine_ip_stop", + "prefix_v6", + "vlan", + "ouverture_ports", + "api_url", + ) + + +class VlanSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Vlan` objects. + """ + + class Meta: + model = machines.Vlan + fields = ( + "vlan_id", + "name", + "comment", + "arp_protect", + "dhcp_snooping", + "dhcpv6_snooping", + "igmp", + "mld", + "api_url", + ) + + +class NasSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Nas` objects. + """ + + class Meta: + model = machines.Nas + fields = ( + "name", + "nas_type", + "machine_type", + "port_access_mode", + "autocapture_mac", + "api_url", + ) + + +class SOASerializer(NamespacedHMSerializer): + """Serialize `machines.models.SOA` objects. + """ + + class Meta: + model = machines.SOA + fields = ("name", "mail", "refresh", "retry", "expire", "ttl", "api_url") + + +class ExtensionSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Extension` objects. + """ + + class Meta: + model = machines.Domain + fields = ("interface_parent", "name", "extension", "cname", "api_url", "ttl") + + +class MxSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Mx` objects. + """ + + class Meta: + model = machines.Mx + fields = ("zone", "priority", "name", "api_url") + + +class DNameSerializer(NamespacedHMSerializer): + """Serialize `machines.models.DName` objects. + """ + + class Meta: + model = machines.DName + fields = ("zone", "alias", "api_url") + + +class NsSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Ns` objects. + """ + + class Meta: + model = machines.Ns + fields = ("zone", "ns", "api_url") + + +class TxtSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Txt` objects. + """ + + class Meta: + model = machines.Txt + fields = ("zone", "field1", "field2", "api_url") + + +class SrvSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Srv` objects. + """ + + class Meta: + model = machines.Srv + fields = ( + "service", + "protocole", + "extension", + "ttl", + "priority", + "weight", + "port", + "target", + "api_url", + ) + + +class SshFpSerializer(NamespacedHMSerializer): + """Serialize `machines.models.SSHFP` objects. + """ + + class Meta: + model = machines.SshFp + field = ("machine", "pub_key_entry", "algo", "comment", "api_url") + + +class InterfaceSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Interface` objects. + """ + + mac_address = serializers.CharField() + active = serializers.BooleanField(source="is_active") + + class Meta: + model = machines.Interface + fields = ( + "ipv4", + "mac_address", + "machine", + "machine_type", + "details", + "port_lists", + "active", + "api_url", + ) + + +class Ipv6ListSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Ipv6List` objects. + """ + + class Meta: + model = machines.Ipv6List + fields = ("ipv6", "interface", "slaac_ip", "api_url") + + +class DomainSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Domain` objects. + """ + + class Meta: + model = machines.Domain + fields = ("interface_parent", "name", "extension", "cname", "api_url") + + +class IpListSerializer(NamespacedHMSerializer): + """Serialize `machines.models.IpList` objects. + """ + + class Meta: + model = machines.IpList + fields = ("ipv4", "ip_type", "need_infra", "api_url") + + +class ServiceSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Service` objects. + """ + + class Meta: + model = machines.Service + fields = ( + "service_type", + "min_time_regen", + "regular_time_regen", + "servers", + "api_url", + ) + + +class ServiceLinkSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Service_link` objects. + """ + + class Meta: + model = machines.Service_link + fields = ( + "service", + "server", + "last_regen", + "asked_regen", + "need_regen", + "api_url", + ) + extra_kwargs = {"api_url": {"view_name": "servicelink-detail"}} + + +class OuverturePortListSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePortList` objects. + """ + + tcp_ports_in = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + udp_ports_in = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + tcp_ports_out = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + udp_ports_out = NamespacedHRField( + view_name="ouvertureport-detail", many=True, read_only=True + ) + + class Meta: + model = machines.OuverturePortList + fields = ( + "name", + "tcp_ports_in", + "udp_ports_in", + "tcp_ports_out", + "udp_ports_out", + "api_url", + ) + + +class OuverturePortSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePort` objects. + """ + + class Meta: + model = machines.OuverturePort + fields = ("begin", "end", "port_list", "protocole", "io", "api_url") + + +class RoleSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePort` objects. + """ + + servers = InterfaceSerializer(read_only=True, many=True) + + class Meta: + model = machines.Role + fields = ("role_type", "servers", "api_url") + + +class ServiceRegenSerializer(NamespacedHMSerializer): + """Serialize the data about the services to regen. + """ + + hostname = serializers.CharField(source="server.domain.name", read_only=True) + service_name = serializers.CharField(source="service.service_type", read_only=True) + need_regen = serializers.BooleanField() + + class Meta: + model = machines.Service_link + fields = ("hostname", "service_name", "need_regen", "api_url") + extra_kwargs = {"api_url": {"view_name": "serviceregen-detail"}} + + +class HostMacIpSerializer(serializers.ModelSerializer): + """Serialize the data about the hostname-ipv4-mac address association + to build the DHCP lease files. + """ + + hostname = serializers.CharField(source="domain.name", read_only=True) + extension = serializers.CharField(source="domain.extension.name", read_only=True) + mac_address = serializers.CharField(read_only=True) + ip_type = serializers.CharField(source="machine_type.ip_type", read_only=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) + + class Meta: + model = machines.Interface + fields = ("hostname", "extension", "mac_address", "ipv4", "ip_type") + + +class FirewallPortListSerializer(serializers.ModelSerializer): + class Meta: + model = machines.OuverturePort + fields = ("begin", "end", "protocole", "io", "show_port") + + +class FirewallOuverturePortListSerializer(serializers.ModelSerializer): + tcp_ports_in = FirewallPortListSerializer(many=True, read_only=True) + udp_ports_in = FirewallPortListSerializer(many=True, read_only=True) + tcp_ports_out = FirewallPortListSerializer(many=True, read_only=True) + udp_ports_out = FirewallPortListSerializer(many=True, read_only=True) + + class Meta: + model = machines.OuverturePortList + fields = ("tcp_ports_in", "udp_ports_in", "tcp_ports_out", "udp_ports_out") + + +class SubnetPortsOpenSerializer(serializers.ModelSerializer): + ouverture_ports = FirewallOuverturePortListSerializer(read_only=True) + + class Meta: + model = machines.IpType + fields = ( + "name", + "domaine_ip_start", + "domaine_ip_stop", + "complete_prefixv6", + "ouverture_ports", + ) + + +class InterfacePortsOpenSerializer(serializers.ModelSerializer): + port_lists = FirewallOuverturePortListSerializer(read_only=True, many=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) + ipv6 = Ipv6ListSerializer(many=True, read_only=True) + + class Meta: + model = machines.Interface + fields = ("port_lists", "ipv4", "ipv6") + + +class SOARecordSerializer(SOASerializer): + """Serialize `machines.models.SOA` objects with the data needed to + generate a SOA DNS record. + """ + + class Meta: + model = machines.SOA + fields = ("name", "mail", "refresh", "retry", "expire", "ttl") + + +class OriginV4RecordSerializer(IpListSerializer): + """Serialize `machines.models.IpList` objects with the data needed to + generate an IPv4 Origin DNS record. + """ + + class Meta(IpListSerializer.Meta): + fields = ("ipv4",) + + +class NSRecordSerializer(NsSerializer): + """Serialize `machines.models.Ns` objects with the data needed to + generate a NS DNS record. + """ + + target = serializers.CharField(source="ns", read_only=True) + + class Meta(NsSerializer.Meta): + fields = ("target", "ttl") + + +class MXRecordSerializer(MxSerializer): + """Serialize `machines.models.Mx` objects with the data needed to + generate a MX DNS record. + """ + + target = serializers.CharField(source="name", read_only=True) + + class Meta(MxSerializer.Meta): + fields = ("target", "priority", "ttl") + + +class TXTRecordSerializer(TxtSerializer): + """Serialize `machines.models.Txt` objects with the data needed to + generate a TXT DNS record. + """ + + class Meta(TxtSerializer.Meta): + fields = ("field1", "field2", "ttl") + + +class SRVRecordSerializer(SrvSerializer): + """Serialize `machines.models.Srv` objects with the data needed to + generate a SRV DNS record. + """ + + target = serializers.CharField(source="target.name", read_only=True) + + class Meta(SrvSerializer.Meta): + fields = ("service", "protocole", "ttl", "priority", "weight", "port", "target") + + +class SSHFPRecordSerializer(SshFpSerializer): + """Serialize `machines.models.SshFp` objects with the data needed to + generate a SSHFP DNS record. + """ + + class Meta(SshFpSerializer.Meta): + fields = ("algo_id", "hash") + + +class SSHFPInterfaceSerializer(serializers.ModelSerializer): + """Serialize `machines.models.Domain` objects with the data needed to + generate a CNAME DNS record. + """ + + hostname = serializers.CharField(source="domain.name", read_only=True) + sshfp = SSHFPRecordSerializer(source="machine.sshfp_set", many=True, read_only=True) + + class Meta: + model = machines.Interface + fields = ("hostname", "sshfp") + + +class ARecordSerializer(serializers.ModelSerializer): + """Serialize `machines.models.Interface` objects with the data needed to + generate a A DNS record. + """ + + hostname = serializers.CharField(source="domain.name", read_only=True) + ipv4 = serializers.CharField(source="ipv4.ipv4", read_only=True) + ttl = serializers.IntegerField(source="domain.ttl", read_only=True) + + class Meta: + model = machines.Interface + fields = ("hostname", "ipv4", "ttl") + + +class AAAARecordSerializer(serializers.ModelSerializer): + """Serialize `machines.models.Interface` objects with the data needed to + generate a AAAA DNS record. + """ + + hostname = serializers.CharField(source="domain.name", read_only=True) + ipv6 = Ipv6ListSerializer(many=True, read_only=True) + ttl = serializers.IntegerField(source="domain.ttl", read_only=True) + + class Meta: + model = machines.Interface + fields = ("hostname", "ipv6", "ttl") + + +class CNAMERecordSerializer(serializers.ModelSerializer): + """Serialize `machines.models.Domain` objects with the data needed to + generate a CNAME DNS record. + """ + + alias = serializers.CharField(source="cname", read_only=True) + hostname = serializers.CharField(source="name", read_only=True) + + class Meta: + model = machines.Domain + fields = ("alias", "hostname", "ttl") + + +class DNAMERecordSerializer(serializers.ModelSerializer): + """Serialize `machines.models.Domain` objects with the data needed to + generate a DNAME DNS record. + """ + + alias = serializers.CharField(read_only=True) + zone = serializers.CharField(read_only=True) + + class Meta: + model = machines.DName + fields = ("alias", "zone", "ttl") + + +class DNSZonesSerializer(serializers.ModelSerializer): + """Serialize the data about DNS Zones. + """ + + soa = SOARecordSerializer() + ns_records = NSRecordSerializer(many=True, source="ns_set") + originv4 = OriginV4RecordSerializer(source="origin") + originv6 = serializers.CharField(source="origin_v6") + mx_records = MXRecordSerializer(many=True, source="mx_set") + txt_records = TXTRecordSerializer(many=True, source="txt_set") + srv_records = SRVRecordSerializer(many=True, source="srv_set") + a_records = ARecordSerializer(many=True, source="get_associated_a_records") + aaaa_records = AAAARecordSerializer(many=True, source="get_associated_aaaa_records") + cname_records = CNAMERecordSerializer( + many=True, source="get_associated_cname_records" + ) + dname_records = DNAMERecordSerializer( + many=True, source="get_associated_dname_records" + ) + sshfp_records = SSHFPInterfaceSerializer( + many=True, source="get_associated_sshfp_records" + ) + + class Meta: + model = machines.Extension + fields = ( + "name", + "soa", + "ns_records", + "originv4", + "originv6", + "mx_records", + "txt_records", + "srv_records", + "a_records", + "aaaa_records", + "cname_records", + "dname_records", + "sshfp_records", + ) + + +class DNSReverseZonesSerializer(serializers.ModelSerializer): + """Serialize the data about DNS Zones. + """ + + soa = SOARecordSerializer(source="extension.soa") + extension = serializers.CharField(source="extension.name", read_only=True) + cidrs = serializers.ListField( + child=serializers.CharField(), source="ip_set_cidrs_as_str", read_only=True + ) + ns_records = NSRecordSerializer(many=True, source="extension.ns_set") + mx_records = MXRecordSerializer(many=True, source="extension.mx_set") + txt_records = TXTRecordSerializer(many=True, source="extension.txt_set") + ptr_records = ARecordSerializer(many=True, source="get_associated_ptr_records") + ptr_v6_records = AAAARecordSerializer( + many=True, source="get_associated_ptr_v6_records" + ) + + class Meta: + model = machines.IpType + fields = ( + "name", + "extension", + "soa", + "ns_records", + "mx_records", + "txt_records", + "ptr_records", + "ptr_v6_records", + "cidrs", + "prefix_v6", + "prefix_v6_length", + ) \ No newline at end of file diff --git a/machines/api/urls.py b/machines/api/urls.py new file mode 100644 index 00000000..3cf82122 --- /dev/null +++ b/machines/api/urls.py @@ -0,0 +1,66 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 . import views + +urls_viewset = [ + (r"machines/machine", views.MachineViewSet, None), + (r"machines/machinetype", views.MachineTypeViewSet, None), + (r"machines/iptype", views.IpTypeViewSet, None), + (r"machines/vlan", views.VlanViewSet, None), + (r"machines/nas", views.NasViewSet, None), + (r"machines/soa", views.SOAViewSet, None), + (r"machines/extension", views.ExtensionViewSet, None), + (r"machines/mx", views.MxViewSet, None), + (r"machines/ns", views.NsViewSet, None), + (r"machines/txt", views.TxtViewSet, None), + (r"machines/dname", views.DNameViewSet, None), + (r"machines/srv", views.SrvViewSet, None), + (r"machines/sshfp", views.SshFpViewSet, None), + (r"machines/interface", views.InterfaceViewSet, None), + (r"machines/ipv6list", views.Ipv6ListViewSet, None), + (r"machines/domain", views.DomainViewSet, None), + (r"machines/iplist", views.IpListViewSet, None), + (r"machines/service", views.ServiceViewSet, None), + (r"machines/servicelink", views.ServiceLinkViewSet, "servicelink"), + (r"machines/ouvertureportlist", views.OuverturePortListViewSet, None), + (r"machines/ouvertureport", views.OuverturePortViewSet, None), + (r"machines/role", views.RoleViewSet, None), + (r"machines/services-regen", views.ServiceRegenViewSet, "serviceregen"), + + # Deprecated + (r"services/regen", views.ServiceRegenViewSet, "serviceregen") +] + +urls_view = [ + (r"machines/hostmacip", views.HostMacIpView), + (r"machines/firewall-subnet-ports", views.SubnetPortsOpenView), + (r"machines/firewall-interface-ports", views.InterfacePortsOpenView), + (r"machines/dns-zones", views.DNSZonesView), + (r"machines/dns-reverse-zones", views.DNSReverseZonesView), + + # Deprecated + (r"dhcp/hostmacip", views.HostMacIpView), + (r"firewall/subnet-ports", views.SubnetPortsOpenView), + (r"firewall/interface-ports", views.InterfacePortsOpenView), + (r"dns/zones", views.DNSZonesView), + (r"dns/reverse-zones", views.DNSReverseZonesView), +] \ No newline at end of file diff --git a/machines/api/views.py b/machines/api/views.py new file mode 100644 index 00000000..3e510d8e --- /dev/null +++ b/machines/api/views.py @@ -0,0 +1,267 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import viewsets, generics + +from . import serializers +import machines.models as machines +from re2o.utils import all_active_interfaces + +class MachineViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Machine` objects. + """ + + queryset = machines.Machine.objects.all() + serializer_class = serializers.MachineSerializer + + +class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.MachineType` objects. + """ + + queryset = machines.MachineType.objects.all() + serializer_class = serializers.MachineTypeSerializer + + +class IpTypeViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.IpType` objects. + """ + + queryset = machines.IpType.objects.all() + serializer_class = serializers.IpTypeSerializer + + +class VlanViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Vlan` objects. + """ + + queryset = machines.Vlan.objects.all() + serializer_class = serializers.VlanSerializer + + +class NasViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Nas` objects. + """ + + queryset = machines.Nas.objects.all() + serializer_class = serializers.NasSerializer + + +class SOAViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.SOA` objects. + """ + + queryset = machines.SOA.objects.all() + serializer_class = serializers.SOASerializer + + +class ExtensionViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Extension` objects. + """ + + queryset = machines.Extension.objects.all() + serializer_class = serializers.ExtensionSerializer + + +class MxViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Mx` objects. + """ + + queryset = machines.Mx.objects.all() + serializer_class = serializers.MxSerializer + + +class NsViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Ns` objects. + """ + + queryset = machines.Ns.objects.all() + serializer_class = serializers.NsSerializer + + +class TxtViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Txt` objects. + """ + + queryset = machines.Txt.objects.all() + serializer_class = serializers.TxtSerializer + + +class DNameViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.DName` objects. + """ + + queryset = machines.DName.objects.all() + serializer_class = serializers.DNameSerializer + + +class SrvViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Srv` objects. + """ + + queryset = machines.Srv.objects.all() + serializer_class = serializers.SrvSerializer + + +class SshFpViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.SshFp` objects. + """ + + queryset = machines.SshFp.objects.all() + serializer_class = serializers.SshFpSerializer + + +class InterfaceViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Interface` objects. + """ + + queryset = machines.Interface.objects.all() + serializer_class = serializers.InterfaceSerializer + + +class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Ipv6List` objects. + """ + + queryset = machines.Ipv6List.objects.all() + serializer_class = serializers.Ipv6ListSerializer + + +class DomainViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Domain` objects. + """ + + queryset = machines.Domain.objects.all() + serializer_class = serializers.DomainSerializer + + +class IpListViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.IpList` objects. + """ + + queryset = machines.IpList.objects.all() + serializer_class = serializers.IpListSerializer + + +class ServiceViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Service` objects. + """ + + queryset = machines.Service.objects.all() + serializer_class = serializers.ServiceSerializer + + +class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Service_link` objects. + """ + + queryset = machines.Service_link.objects.all() + serializer_class = serializers.ServiceLinkSerializer + + +class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.OuverturePortList` + objects. + """ + + queryset = machines.OuverturePortList.objects.all() + serializer_class = serializers.OuverturePortListSerializer + + +class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.OuverturePort` objects. + """ + + queryset = machines.OuverturePort.objects.all() + serializer_class = serializers.OuverturePortSerializer + + +class RoleViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `machines.models.Machine` objects. + """ + + queryset = machines.Role.objects.all() + serializer_class = serializers.RoleSerializer + + +class ServiceRegenViewSet(viewsets.ModelViewSet): + """Exposes list and details of the services to regen + """ + + serializer_class = serializers.ServiceRegenSerializer + + def get_queryset(self): + queryset = machines.Service_link.objects.select_related( + "server__domain" + ).select_related("service") + if "hostname" in self.request.GET: + hostname = self.request.GET["hostname"] + queryset = queryset.filter(server__domain__name__iexact=hostname) + return queryset + + +class HostMacIpView(generics.ListAPIView): + """Exposes the associations between hostname, mac address and IPv4 in + order to build the DHCP lease files. + """ + + serializer_class = serializers.HostMacIpSerializer + + def get_queryset(self): + return all_active_interfaces() + + +class SubnetPortsOpenView(generics.ListAPIView): + queryset = machines.IpType.objects.all() + serializer_class = serializers.SubnetPortsOpenSerializer + + +class InterfacePortsOpenView(generics.ListAPIView): + queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct() + serializer_class = serializers.InterfacePortsOpenSerializer + +class DNSZonesView(generics.ListAPIView): + """Exposes the detailed information about each extension (hostnames, + IPs, DNS records, etc.) in order to build the DNS zone files. + """ + + queryset = ( + machines.Extension.objects.prefetch_related("soa") + .prefetch_related("ns_set") + .prefetch_related("ns_set__ns") + .prefetch_related("origin") + .prefetch_related("mx_set") + .prefetch_related("mx_set__name") + .prefetch_related("txt_set") + .prefetch_related("srv_set") + .prefetch_related("srv_set__target") + .all() + ) + serializer_class = serializers.DNSZonesSerializer + + +class DNSReverseZonesView(generics.ListAPIView): + """Exposes the detailed information about each extension (hostnames, + IPs, DNS records, etc.) in order to build the DNS zone files. + """ + + queryset = machines.IpType.objects.all() + serializer_class = serializers.DNSReverseZonesSerializer \ No newline at end of file diff --git a/preferences/api/__init__.py b/preferences/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/preferences/api/serializers.py b/preferences/api/serializers.py new file mode 100644 index 00000000..d7223028 --- /dev/null +++ b/preferences/api/serializers.py @@ -0,0 +1,174 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import serializers + +import preferences.models as preferences +from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer + +class OptionalUserSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.OptionalUser` objects. + """ + + tel_mandatory = serializers.BooleanField(source="is_tel_mandatory") + shell_default = serializers.StringRelatedField() + + class Meta: + model = preferences.OptionalUser + fields = ( + "tel_mandatory", + "gpg_fingerprint", + "all_can_create_club", + "self_adhesion", + "shell_default", + "self_change_shell", + "local_email_accounts_enabled", + "local_email_domain", + "max_email_address", + ) + + +class OptionalMachineSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.OptionalMachine` objects. + """ + + class Meta: + model = preferences.OptionalMachine + fields = ( + "password_machine", + "max_lambdauser_interfaces", + "max_lambdauser_aliases", + "ipv6_mode", + "create_machine", + "ipv6", + "default_dns_ttl" + ) + + +class OptionalTopologieSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.OptionalTopologie` objects. + """ + + switchs_management_interface_ip = serializers.CharField() + + class Meta: + model = preferences.OptionalTopologie + fields = ( + "switchs_ip_type", + "switchs_web_management", + "switchs_web_management_ssl", + "switchs_rest_management", + "switchs_management_utils", + "switchs_management_interface_ip", + "provision_switchs_enabled", + "switchs_provision", + "switchs_management_sftp_creds", + ) + + +class RadiusOptionSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.RadiusOption` objects + """ + + class Meta: + model = preferences.RadiusOption + fields = ( + "radius_general_policy", + "unknown_machine", + "unknown_machine_vlan", + "unknown_port", + "unknown_port_vlan", + "unknown_room", + "unknown_room_vlan", + "non_member", + "non_member_vlan", + "banned", + "banned_vlan", + "vlan_decision_ok", + ) + + +class GeneralOptionSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.GeneralOption` objects. + """ + + class Meta: + model = preferences.GeneralOption + fields = ( + "general_message_fr", + "general_message_en", + "search_display_page", + "pagination_number", + "pagination_large_number", + "req_expire_hrs", + "site_name", + "main_site_url", + "email_from", + "GTU_sum_up", + "GTU", + ) + + +class HomeServiceSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.Service` objects. + """ + + class Meta: + model = preferences.Service + fields = ("name", "url", "description", "image", "api_url") + extra_kwargs = {"api_url": {"view_name": "homeservice-detail"}} + + +class AssoOptionSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.AssoOption` objects. + """ + + class Meta: + model = preferences.AssoOption + fields = ( + "name", + "siret", + "adresse1", + "adresse2", + "contact", + "telephone", + "pseudo", + "utilisateur_asso", + "description", + ) + + +class HomeOptionSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.HomeOption` objects. + """ + + class Meta: + model = preferences.HomeOption + fields = ("facebook_url", "twitter_url", "twitter_account_name") + + +class MailMessageOptionSerializer(NamespacedHMSerializer): + """Serialize `preferences.models.MailMessageOption` objects. + """ + + class Meta: + model = preferences.MailMessageOption + fields = ("welcome_mail_fr", "welcome_mail_en") \ No newline at end of file diff --git a/preferences/api/urls.py b/preferences/api/urls.py new file mode 100644 index 00000000..b9ebf859 --- /dev/null +++ b/preferences/api/urls.py @@ -0,0 +1,37 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 . import views + +urls_viewset = [ + (r"preferences/service", views.HomeServiceViewSet, "homeservice") +] + +urls_view = [ + (r"preferences/optionaluser", views.OptionalUserView), + (r"preferences/optionalmachine", views.OptionalMachineView), + (r"preferences/optionaltopologie", views.OptionalTopologieView), + (r"preferences/radiusoption", views.RadiusOptionView), + (r"preferences/generaloption", views.GeneralOptionView), + (r"preferences/assooption", views.AssoOptionView), + (r"preferences/homeoption", views.HomeOptionView), + (r"preferences/mailmessageoption", views.MailMessageOptionView) +] \ No newline at end of file diff --git a/preferences/api/views.py b/preferences/api/views.py new file mode 100644 index 00000000..6bce71b2 --- /dev/null +++ b/preferences/api/views.py @@ -0,0 +1,130 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import viewsets, generics + +from . import serializers +import preferences.models as preferences +from api.permissions import ACLPermission + + +class OptionalUserView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.OptionalUser.can_view_all]} + serializer_class = serializers.OptionalUserSerializer + + def get_object(self): + return preferences.OptionalUser.objects.first() + + +class OptionalMachineView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.OptionalMachine` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.OptionalMachine.can_view_all]} + serializer_class = serializers.OptionalMachineSerializer + + def get_object(self): + return preferences.OptionalMachine.objects.first() + + +class OptionalTopologieView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.OptionalTopologie` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]} + serializer_class = serializers.OptionalTopologieSerializer + + def get_object(self): + return preferences.OptionalTopologie.objects.first() + + +class RadiusOptionView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.OptionalTopologie` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.RadiusOption.can_view_all]} + serializer_class = serializers.RadiusOptionSerializer + + def get_object(self): + return preferences.RadiusOption.objects.first() + + +class GeneralOptionView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.GeneralOption` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.GeneralOption.can_view_all]} + serializer_class = serializers.GeneralOptionSerializer + + def get_object(self): + return preferences.GeneralOption.objects.first() + + +class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `preferences.models.Service` objects. + """ + + queryset = preferences.Service.objects.all() + serializer_class = serializers.HomeServiceSerializer + + +class AssoOptionView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.AssoOption` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.AssoOption.can_view_all]} + serializer_class = serializers.AssoOptionSerializer + + def get_object(self): + return preferences.AssoOption.objects.first() + + +class HomeOptionView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.HomeOption` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.HomeOption.can_view_all]} + serializer_class = serializers.HomeOptionSerializer + + def get_object(self): + return preferences.HomeOption.objects.first() + + +class MailMessageOptionView(generics.RetrieveAPIView): + """Exposes details of `preferences.models.MailMessageOption` settings. + """ + + permission_classes = (ACLPermission,) + perms_map = {"GET": [preferences.MailMessageOption.can_view_all]} + serializer_class = serializers.MailMessageOptionSerializer + + def get_object(self): + return preferences.MailMessageOption.objects.first() \ No newline at end of file diff --git a/topologie/api/__init__.py b/topologie/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/topologie/api/serializers.py b/topologie/api/serializers.py new file mode 100644 index 00000000..18785ebb --- /dev/null +++ b/topologie/api/serializers.py @@ -0,0 +1,323 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import serializers + +import topologie.models as topologie +import machines.models as machines +from machines.api.serializers import VlanSerializer, Ipv6ListSerializer +from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer + + +class StackSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Stack` objects + """ + + class Meta: + model = topologie.Stack + fields = ( + "name", + "stack_id", + "details", + "member_id_min", + "member_id_max", + "api_url", + ) + + +class AccessPointSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.AccessPoint` objects + """ + + class Meta: + model = topologie.AccessPoint + fields = ("user", "name", "active", "location", "api_url") + + +class SwitchSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Switch` objects + """ + + port_amount = serializers.IntegerField(source="number") + + class Meta: + model = topologie.Switch + fields = ( + "user", + "name", + "active", + "port_amount", + "stack", + "stack_member_id", + "model", + "switchbay", + "api_url", + ) + + +class ServerSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Server` objects + """ + + class Meta: + model = topologie.Server + fields = ("user", "name", "active", "api_url") + + +class ModelSwitchSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.ModelSwitch` objects + """ + + class Meta: + model = topologie.ModelSwitch + fields = ("reference", "constructor", "api_url") + + +class ConstructorSwitchSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.ConstructorSwitch` objects + """ + + class Meta: + model = topologie.ConstructorSwitch + fields = ("name", "api_url") + + +class SwitchBaySerializer(NamespacedHMSerializer): + """Serialize `topologie.models.SwitchBay` objects + """ + + class Meta: + model = topologie.SwitchBay + fields = ("name", "building", "info", "api_url") + + +class BuildingSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Building` objects + """ + + class Meta: + model = topologie.Building + fields = ("name", "api_url") + + +class SwitchPortSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Port` objects + """ + + get_port_profile = NamespacedHIField(view_name="portprofile-detail", read_only=True) + + class Meta: + model = topologie.Port + fields = ( + "switch", + "port", + "room", + "machine_interface", + "related", + "custom_profile", + "state", + "get_port_profile", + "details", + "api_url", + ) + extra_kwargs = { + "related": {"view_name": "switchport-detail"}, + "api_url": {"view_name": "switchport-detail"}, + } + + +class PortProfileSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Room` objects + """ + + class Meta: + model = topologie.PortProfile + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "api_url", + ) + + +class RoomSerializer(NamespacedHMSerializer): + """Serialize `topologie.models.Room` objects + """ + + class Meta: + model = topologie.Room + fields = ("name", "details", "api_url") + + +class PortProfileSerializer(NamespacedHMSerializer): + vlan_untagged = VlanSerializer(read_only=True) + + class Meta: + model = topologie.PortProfile + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "vlan_untagged", + "vlan_tagged", + ) + + +class InterfaceVlanSerializer(NamespacedHMSerializer): + domain = serializers.CharField(read_only=True) + ipv4 = serializers.CharField(read_only=True) + ipv6 = Ipv6ListSerializer(read_only=True, many=True) + vlan_id = serializers.IntegerField( + source="machine_type.ip_type.vlan.vlan_id", read_only=True + ) + + class Meta: + model = machines.Interface + fields = ("ipv4", "ipv6", "domain", "vlan_id") + + +class InterfaceRoleSerializer(NamespacedHMSerializer): + interface = InterfaceVlanSerializer( + source="machine.interface_set", read_only=True, many=True + ) + + class Meta: + model = machines.Interface + fields = ("interface",) + + +class RoleSerializer(NamespacedHMSerializer): + """Serialize `machines.models.OuverturePort` objects. + """ + + servers = InterfaceRoleSerializer(read_only=True, many=True) + + class Meta: + model = machines.Role + fields = ("role_type", "servers", "specific_role") + + +class VlanPortSerializer(NamespacedHMSerializer): + class Meta: + model = machines.Vlan + fields = ("vlan_id", "name") + + +class ProfilSerializer(NamespacedHMSerializer): + vlan_untagged = VlanSerializer(read_only=True) + vlan_tagged = VlanPortSerializer(read_only=True, many=True) + + class Meta: + model = topologie.PortProfile + fields = ( + "name", + "profil_default", + "vlan_untagged", + "vlan_tagged", + "radius_type", + "radius_mode", + "speed", + "mac_limit", + "flow_control", + "dhcp_snooping", + "dhcpv6_snooping", + "arp_protect", + "ra_guard", + "loop_protect", + "vlan_untagged", + "vlan_tagged", + ) + + +class ModelSwitchSerializer(NamespacedHMSerializer): + constructor = serializers.CharField(read_only=True) + + class Meta: + model = topologie.ModelSwitch + fields = ("reference", "firmware", "constructor") + + +class SwitchBaySerializer(NamespacedHMSerializer): + class Meta: + model = topologie.SwitchBay + fields = ("name",) + + +class PortsSerializer(NamespacedHMSerializer): + """Serialize `machines.models.Ipv6List` objects. + """ + + get_port_profile = ProfilSerializer(read_only=True) + + class Meta: + model = topologie.Port + fields = ("state", "port", "pretty_name", "get_port_profile") + + +class SwitchPortSerializer(serializers.ModelSerializer): + """Serialize the data about the switches""" + + ports = PortsSerializer(many=True, read_only=True) + model = ModelSwitchSerializer(read_only=True) + switchbay = SwitchBaySerializer(read_only=True) + + class Meta: + model = topologie.Switch + fields = ( + "short_name", + "model", + "switchbay", + "ports", + "ipv4", + "ipv6", + "interfaces_subnet", + "interfaces6_subnet", + "automatic_provision", + "rest_enabled", + "web_management_enabled", + "get_radius_key_value", + "get_management_cred_value", + "get_radius_servers", + "list_modules", + ) diff --git a/topologie/api/urls.py b/topologie/api/urls.py new file mode 100644 index 00000000..c5c93f8f --- /dev/null +++ b/topologie/api/urls.py @@ -0,0 +1,46 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 . import views + +urls_viewset = [ + (r"topologie/stack", views.StackViewSet, None), + (r"topologie/acesspoint", views.AccessPointViewSet, None), + (r"topologie/switch", views.SwitchViewSet, None), + (r"topologie/server", views.ServerViewSet, None), + (r"topologie/modelswitch", views.ModelSwitchViewSet, None), + (r"topologie/constructorswitch", views.ConstructorSwitchViewSet, None), + (r"topologie/switchbay", views.SwitchBayViewSet, None), + (r"topologie/building", views.BuildingViewSet, None), + (r"topologie/switchport", views.SwitchPortViewSet, "switchport"), + (r"topologie/portprofile", views.PortProfileViewSet, "portprofile"), + (r"topologie/room", views.RoomViewSet, None) +] + +urls_view = [ + # (r"topologie/portprofile", views.PortProfileViewSet) + (r"topologie/switchs-ports-config", views.SwitchPortView), + (r"topologie/switchs-role", views.RoleView), + + # Deprecated + (r"switchs/ports-config", views.SwitchPortView), + (r"switchs/role", views.RoleView), +] \ No newline at end of file diff --git a/topologie/api/views.py b/topologie/api/views.py new file mode 100644 index 00000000..5e937cda --- /dev/null +++ b/topologie/api/views.py @@ -0,0 +1,149 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import viewsets, generics + +from . import serializers +import topologie.models as topologie +import machines.models as machines + + +class StackViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Stack` objects. + """ + + queryset = topologie.Stack.objects.all() + serializer_class = serializers.StackSerializer + + +class AccessPointViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.AccessPoint` objects. + """ + + queryset = topologie.AccessPoint.objects.all() + serializer_class = serializers.AccessPointSerializer + + +class SwitchViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Switch` objects. + """ + + queryset = topologie.Switch.objects.all() + serializer_class = serializers.SwitchSerializer + + +class ServerViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Server` objects. + """ + + queryset = topologie.Server.objects.all() + serializer_class = serializers.ServerSerializer + + +class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.ModelSwitch` objects. + """ + + queryset = topologie.ModelSwitch.objects.all() + serializer_class = serializers.ModelSwitchSerializer + + +class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.ConstructorSwitch` + objects. + """ + + queryset = topologie.ConstructorSwitch.objects.all() + serializer_class = serializers.ConstructorSwitchSerializer + + +class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.SwitchBay` objects. + """ + + queryset = topologie.SwitchBay.objects.all() + serializer_class = serializers.SwitchBaySerializer + + +class BuildingViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Building` objects. + """ + + queryset = topologie.Building.objects.all() + serializer_class = serializers.BuildingSerializer + + +class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Port` objects. + """ + + queryset = topologie.Port.objects.all() + serializer_class = serializers.SwitchPortSerializer + + +class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.PortProfile` objects. + """ + + queryset = topologie.PortProfile.objects.all() + serializer_class = serializers.PortProfileSerializer + + +class RoomViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.Room` objects. + """ + + queryset = topologie.Room.objects.all() + serializer_class = serializers.RoomSerializer + + +class PortProfileViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `topologie.models.PortProfile` objects. + """ + + queryset = topologie.PortProfile.objects.all() + serializer_class = serializers.PortProfileSerializer + + +class SwitchPortView(generics.ListAPIView): + """Output each port of a switch, to be serialized with + additionnal informations (profiles etc) + """ + + queryset = ( + topologie.Switch.objects.all() + .select_related("switchbay") + .select_related("model__constructor") + .prefetch_related("ports__custom_profile__vlan_tagged") + .prefetch_related("ports__custom_profile__vlan_untagged") + .prefetch_related("ports__machine_interface__domain__extension") + .prefetch_related("ports__room") + ) + + serializer_class = serializers.SwitchPortSerializer + + +class RoleView(generics.ListAPIView): + """Output of roles for each server + """ + + queryset = machines.Role.objects.all().prefetch_related("servers") + serializer_class = serializers.RoleSerializer \ No newline at end of file diff --git a/users/api/__init__.py b/users/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/users/api/serializers.py b/users/api/serializers.py new file mode 100644 index 00000000..e4c6c7e0 --- /dev/null +++ b/users/api/serializers.py @@ -0,0 +1,244 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import serializers + +import users.models as users +from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer + +class UserSerializer(NamespacedHMSerializer): + """Serialize `users.models.User` objects. + """ + + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") + + class Meta: + model = users.User + fields = ( + "surname", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "solde", + "access", + "end_access", + "uid", + "class_type", + "api_url", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} + + +class ClubSerializer(NamespacedHMSerializer): + """Serialize `users.models.Club` objects. + """ + + name = serializers.CharField(source="surname") + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") + + class Meta: + model = users.Club + fields = ( + "name", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "solde", + "room", + "access", + "end_access", + "administrators", + "members", + "mailing", + "uid", + "api_url", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} + + +class AdherentSerializer(NamespacedHMSerializer): + """Serialize `users.models.Adherent` objects. + """ + + access = serializers.BooleanField(source="has_access") + uid = serializers.IntegerField(source="uid_number") + + class Meta: + model = users.Adherent + fields = ( + "name", + "surname", + "pseudo", + "email", + "local_email_redirect", + "local_email_enabled", + "school", + "shell", + "comment", + "state", + "registered", + "telephone", + "room", + "solde", + "access", + "end_access", + "uid", + "api_url", + "gid", + ) + extra_kwargs = {"shell": {"view_name": "shell-detail"}} + + +class BasicUserSerializer(NamespacedHMSerializer): + """Serialize 'users.models.User' minimal infos""" + + uid = serializers.IntegerField(source="uid_number") + gid = serializers.IntegerField(source="gid_number") + + class Meta: + model = users.User + fields = ("pseudo", "uid", "gid") + + +class ServiceUserSerializer(NamespacedHMSerializer): + """Serialize `users.models.ServiceUser` objects. + """ + + class Meta: + model = users.ServiceUser + fields = ("pseudo", "access_group", "comment", "api_url") + + +class SchoolSerializer(NamespacedHMSerializer): + """Serialize `users.models.School` objects. + """ + + class Meta: + model = users.School + fields = ("name", "api_url") + + +class ListRightSerializer(NamespacedHMSerializer): + """Serialize `users.models.ListRight` objects. + """ + + class Meta: + model = users.ListRight + fields = ("unix_name", "gid", "critical", "details", "api_url") + + +class ShellSerializer(NamespacedHMSerializer): + """Serialize `users.models.ListShell` objects. + """ + + class Meta: + model = users.ListShell + fields = ("shell", "api_url") + extra_kwargs = {"api_url": {"view_name": "shell-detail"}} + + +class BanSerializer(NamespacedHMSerializer): + """Serialize `users.models.Ban` objects. + """ + + active = serializers.BooleanField(source="is_active") + + class Meta: + model = users.Ban + fields = ( + "user", + "raison", + "date_start", + "date_end", + "state", + "active", + "api_url", + ) + + +class WhitelistSerializer(NamespacedHMSerializer): + """Serialize `users.models.Whitelist` objects. + """ + + active = serializers.BooleanField(source="is_active") + + class Meta: + model = users.Whitelist + fields = ("user", "raison", "date_start", "date_end", "active", "api_url") + + +class EMailAddressSerializer(NamespacedHMSerializer): + """Serialize `users.models.EMailAddress` objects. + """ + + user = serializers.CharField(source="user.pseudo", read_only=True) + + class Meta: + model = users.EMailAddress + fields = ("user", "local_part", "complete_email_address", "api_url") + + +class LocalEmailUsersSerializer(NamespacedHMSerializer): + email_address = EMailAddressSerializer(read_only=True, many=True) + + class Meta: + model = users.User + fields = ( + "local_email_enabled", + "local_email_redirect", + "email_address", + "email", + ) + + +class MailingMemberSerializer(UserSerializer): + """Serialize the data about a mailing member. + """ + + class Meta(UserSerializer.Meta): + fields = ("name", "pseudo", "get_mail") + + +class MailingSerializer(ClubSerializer): + """Serialize the data about a mailing. + """ + + members = MailingMemberSerializer(many=True) + admins = MailingMemberSerializer(source="administrators", many=True) + + class Meta(ClubSerializer.Meta): + fields = ("name", "members", "admins") \ No newline at end of file diff --git a/users/api/urls.py b/users/api/urls.py new file mode 100644 index 00000000..027793d3 --- /dev/null +++ b/users/api/urls.py @@ -0,0 +1,49 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 . import views + +urls_viewset = [ + (r"users/user", views.UserViewSet, "user"), + (r"users/homecreation", views.HomeCreationViewSet, "homecreation"), + (r"users/normaluser", views.NormalUserViewSet, "normaluser"), + (r"users/criticaluser", views.CriticalUserViewSet, "criticaluser"), + (r"users/club", views.ClubViewSet, None), + (r"users/adherent", views.AdherentViewSet, None), + (r"users/serviceuser", views.ServiceUserViewSet, None), + (r"users/school", views.SchoolViewSet, None), + (r"users/listright", views.ListRightViewSet, None), + (r"users/shell", views.ShellViewSet, None), + (r"users/ban", views.BanViewSet, None), + (r"users/whitelist", views.WhitelistViewSet, None), + (r"users/emailaddress", views.EMailAddressViewSet, None) +] + +urls_view = [ + (r"users/localemail", views.LocalEmailUsersView), + (r"users/mailing-standard", views.StandardMailingView), + (r"users/mailing-club", views.ClubMailingView), + + # Deprecated + (r"localemail/users", views.LocalEmailUsersView), + (r"mailing/standard", views.StandardMailingView), + (r"mailing/club", views.ClubMailingView), +] \ No newline at end of file diff --git a/users/api/views.py b/users/api/views.py new file mode 100644 index 00000000..8a1a1280 --- /dev/null +++ b/users/api/views.py @@ -0,0 +1,191 @@ +# -*- 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 © 2018 Maël Kervella +# +# 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 rest_framework import viewsets, generics, views +from django.db.models import Q + +from . import serializers +from api.pagination import PageSizedPagination +from api.permissions import ACLPermission +from re2o.utils import all_has_access +import users.models as users +import preferences.models as preferences + + +class UserViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.Users` objects. + """ + + queryset = users.User.objects.all() + serializer_class = serializers.UserSerializer + + +class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes infos of `users.models.Users` objects to create homes. + """ + + queryset = users.User.objects.exclude( + Q(state=users.User.STATE_DISABLED) + | Q(state=users.User.STATE_NOT_YET_ACTIVE) + | Q(state=users.User.STATE_FULL_ARCHIVE) + ) + serializer_class = serializers.BasicUserSerializer + + +class NormalUserViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes infos of `users.models.Users`without specific rights objects.""" + + queryset = users.User.objects.exclude(groups__listright__critical=True).distinct() + serializer_class = serializers.BasicUserSerializer + + +class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes infos of `users.models.Users`without specific rights objects.""" + + queryset = users.User.objects.filter(groups__listright__critical=True).distinct() + serializer_class = serializers.BasicUserSerializer + + +class ClubViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.Club` objects. + """ + + queryset = users.Club.objects.all() + serializer_class = serializers.ClubSerializer + + +class AdherentViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.Adherent` objects. + """ + + queryset = users.Adherent.objects.all() + serializer_class = serializers.AdherentSerializer + + +class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.ServiceUser` objects. + """ + + queryset = users.ServiceUser.objects.all() + serializer_class = serializers.ServiceUserSerializer + + +class SchoolViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.School` objects. + """ + + queryset = users.School.objects.all() + serializer_class = serializers.SchoolSerializer + + +class ListRightViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.ListRight` objects. + """ + + queryset = users.ListRight.objects.all() + serializer_class = serializers.ListRightSerializer + + +class ShellViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.ListShell` objects. + """ + + queryset = users.ListShell.objects.all() + serializer_class = serializers.ShellSerializer + + +class BanViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.Ban` objects. + """ + + queryset = users.Ban.objects.all() + serializer_class = serializers.BanSerializer + + +class WhitelistViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.Whitelist` objects. + """ + + queryset = users.Whitelist.objects.all() + serializer_class = serializers.WhitelistSerializer + + +class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet): + """Exposes list and details of `users.models.EMailAddress` objects. + """ + + serializer_class = serializers.EMailAddressSerializer + queryset = users.EMailAddress.objects.none() + + def get_queryset(self): + if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): + return users.EMailAddress.objects.filter(user__local_email_enabled=True) + else: + return users.EMailAddress.objects.none() + + +class LocalEmailUsersView(generics.ListAPIView): + """Exposes all the aliases of the users that activated the internal address + """ + + serializer_class = serializers.LocalEmailUsersSerializer + + def get_queryset(self): + if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"): + return users.User.objects.filter(local_email_enabled=True) + else: + return users.User.objects.none() + + +class StandardMailingView(views.APIView): + """Exposes list and details of standard mailings (name and members) in + order to building the corresponding mailing lists. + """ + + pagination_class = PageSizedPagination + permission_classes = (ACLPermission,) + perms_map = {"GET": [users.User.can_view_all]} + + def get(self, request, format=None): + adherents_data = serializers.MailingMemberSerializer( + all_has_access(), many=True + ).data + + data = [{"name": "adherents", "members": adherents_data}] + groups = Group.objects.all() + for group in groups: + group_data = serializers.MailingMemberSerializer( + group.user_set.all(), many=True + ).data + data.append({"name": group.name, "members": group_data}) + + paginator = self.pagination_class() + paginator.paginate_queryset(data, request) + return paginator.get_paginated_response(data) + + +class ClubMailingView(generics.ListAPIView): + """Exposes list and details of club mailings (name, members and admins) in + order to build the corresponding mailing lists. + """ + + queryset = users.Club.objects.all() + serializer_class = serializers.MailingSerializer \ No newline at end of file From 77b0aff00718041c36bd4263a4bc4d6d2458a09b Mon Sep 17 00:00:00 2001 From: chapeau Date: Tue, 21 Apr 2020 12:00:52 +0000 Subject: [PATCH 138/490] Clear api generation --- api/serializers.py | 19 ------------------- api/urls.py | 28 +++++++++++++--------------- api/views.py | 7 ------- cotisations/api/serializers.py | 22 ++++++++++++++++++++++ cotisations/api/urls.py | 5 ++++- cotisations/api/views.py | 9 +++++++++ 6 files changed, 48 insertions(+), 42 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 6419d9d2..4b0cd4af 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -64,23 +64,4 @@ class NamespacedHMSerializer(serializers.HyperlinkedModelSerializer): -class ReminderUsersSerializer(UserSerializer): - """Serialize the data about a mailing member. - """ - - class Meta(UserSerializer.Meta): - fields = ("get_full_name", "get_mail") - - -class ReminderSerializer(serializers.ModelSerializer): - """ - Serialize the data about a reminder - """ - - users_to_remind = ReminderUsersSerializer(many=True) - - class Meta: - model = preferences.Reminder - fields = ("days", "message", "users_to_remind") - diff --git a/api/urls.py b/api/urls.py index 50681770..624b838b 100644 --- a/api/urls.py +++ b/api/urls.py @@ -28,25 +28,24 @@ can be generated automatically. """ from django.conf.urls import url, include +from importlib import import_module from . import views from .routers import AllViewsRouter -from cotisations.api.urls import urls_viewset as urls_viewset_cotisations -from cotisations.api.urls import urls_view as urls_view_cotisations -from machines.api.urls import urls_viewset as urls_viewset_machines -from machines.api.urls import urls_view as urls_view_machines -from preferences.api.urls import urls_viewset as urls_viewset_preferences -from preferences.api.urls import urls_view as urls_view_preferences -from topologie.api.urls import urls_viewset as urls_viewset_topologie -from topologie.api.urls import urls_view as urls_view_topologie -from users.api.urls import urls_viewset as urls_viewset_users -from users.api.urls import urls_view as urls_view_users - -urls_viewset = urls_viewset_cotisations + urls_viewset_machines + urls_viewset_preferences + urls_viewset_topologie + urls_viewset_users -urls_view = urls_view_cotisations + urls_view_machines + urls_view_preferences + urls_view_topologie + urls_view_users +from django.conf import settings router = AllViewsRouter() +urls_viewset = [] +urls_view = [] + +for app in settings.INSTALLED_APPS: + try: + module = import_module(".api.urls", package=app) + urls_viewset += getattr(module, "urls_viewset", []) + urls_view += getattr(module, "urls_view", []) + except ImportError: + continue for _url, viewset, name in urls_viewset: if name == None: @@ -57,8 +56,7 @@ for _url, viewset, name in urls_viewset: for _url, view in urls_view: router.register_view(_url, view) -# Reminder -router.register_view(r"reminder/get-users", views.ReminderView), + # TOKEN AUTHENTICATION router.register_view(r"token-auth", views.ObtainExpiringAuthToken) diff --git a/api/views.py b/api/views.py index d17622fc..5ec0db1d 100644 --- a/api/views.py +++ b/api/views.py @@ -48,13 +48,6 @@ from .permissions import ACLPermission -class ReminderView(generics.ListAPIView): - """Output for users to remind an end of their subscription. - """ - - queryset = preferences.Reminder.objects.all() - serializer_class = serializers.ReminderSerializer - class ObtainExpiringAuthToken(ObtainAuthToken): """Exposes a view to obtain a authentication token. diff --git a/cotisations/api/serializers.py b/cotisations/api/serializers.py index 138f5b93..d33c9f7e 100644 --- a/cotisations/api/serializers.py +++ b/cotisations/api/serializers.py @@ -22,7 +22,9 @@ from rest_framework import serializers import cotisations.models as cotisations +import preferences.models as preferences from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer +from users.api.serializers import UserSerializer class FactureSerializer(NamespacedHMSerializer): @@ -103,3 +105,23 @@ class CotisationSerializer(NamespacedHMSerializer): class Meta: model = cotisations.Cotisation fields = ("vente", "type_cotisation", "date_start", "date_end", "api_url") + + +class ReminderUsersSerializer(UserSerializer): + """Serialize the data about a mailing member. + """ + + class Meta(UserSerializer.Meta): + fields = ("get_full_name", "get_mail") + + +class ReminderSerializer(serializers.ModelSerializer): + """ + Serialize the data about a reminder + """ + + users_to_remind = ReminderUsersSerializer(many=True) + + class Meta: + model = preferences.Reminder + fields = ("days", "message", "users_to_remind") \ No newline at end of file diff --git a/cotisations/api/urls.py b/cotisations/api/urls.py index 574c3cd2..33834b26 100644 --- a/cotisations/api/urls.py +++ b/cotisations/api/urls.py @@ -31,5 +31,8 @@ urls_viewset = [ ] urls_view = [ - # (r"reminder/get-users", views.ReminderView), + (r"cotisations/reminder-get-users", views.ReminderView), + + # Deprecated + (r"reminder/get-users", views.ReminderView), ] \ No newline at end of file diff --git a/cotisations/api/views.py b/cotisations/api/views.py index b7f5d13b..246c64a9 100644 --- a/cotisations/api/views.py +++ b/cotisations/api/views.py @@ -23,6 +23,7 @@ from rest_framework import viewsets, generics from . import serializers import cotisations.models as cotisations +import preferences.models as preferences class FactureViewSet(viewsets.ReadOnlyModelViewSet): """Exposes list and details of `cotisations.models.Facture` objects. @@ -78,3 +79,11 @@ class CotisationViewSet(viewsets.ReadOnlyModelViewSet): queryset = cotisations.Cotisation.objects.all() serializer_class = serializers.CotisationSerializer + + +class ReminderView(generics.ListAPIView): + """Output for users to remind an end of their subscription. + """ + + queryset = preferences.Reminder.objects.all() + serializer_class = serializers.ReminderSerializer \ No newline at end of file From cf7e293a1cb5012795e715fa9ef88dbca9bfc276 Mon Sep 17 00:00:00 2001 From: chapeau Date: Tue, 21 Apr 2020 17:19:16 +0200 Subject: [PATCH 139/490] Fix of copy/paste errors --- machines/api/serializers.py | 6 +++--- users/api/urls.py | 2 +- users/api/views.py | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/machines/api/serializers.py b/machines/api/serializers.py index eeebb062..9231a995 100644 --- a/machines/api/serializers.py +++ b/machines/api/serializers.py @@ -107,12 +107,12 @@ class SOASerializer(NamespacedHMSerializer): class ExtensionSerializer(NamespacedHMSerializer): - """Serialize `machines.models.Extension` objects. + """Serialize machines.models.Extension objects. """ class Meta: - model = machines.Domain - fields = ("interface_parent", "name", "extension", "cname", "api_url", "ttl") + model = machines.Extension + fields = ("name", "need_infra", "origin", "origin_v6", "soa", "api_url") class MxSerializer(NamespacedHMSerializer): diff --git a/users/api/urls.py b/users/api/urls.py index 027793d3..ef2b01b1 100644 --- a/users/api/urls.py +++ b/users/api/urls.py @@ -31,7 +31,7 @@ urls_viewset = [ (r"users/serviceuser", views.ServiceUserViewSet, None), (r"users/school", views.SchoolViewSet, None), (r"users/listright", views.ListRightViewSet, None), - (r"users/shell", views.ShellViewSet, None), + (r"users/shell", views.ShellViewSet, "shell"), (r"users/ban", views.BanViewSet, None), (r"users/whitelist", views.WhitelistViewSet, None), (r"users/emailaddress", views.EMailAddressViewSet, None) diff --git a/users/api/views.py b/users/api/views.py index 8a1a1280..57571f70 100644 --- a/users/api/views.py +++ b/users/api/views.py @@ -21,6 +21,7 @@ from rest_framework import viewsets, generics, views from django.db.models import Q +from django.contrib.auth.models import Group from . import serializers from api.pagination import PageSizedPagination From a370ae066ec9adf726dc62c697918faec5632f3c Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 21 Apr 2020 17:37:17 +0200 Subject: [PATCH 140/490] Fix #98 --- re2o/utils.py | 14 +++ templates/base.html | 2 + users/templates/users/edit_listright.html | 141 ++++++++++++++++++++++ users/views.py | 113 +++++++---------- 4 files changed, 200 insertions(+), 70 deletions(-) create mode 100644 users/templates/users/edit_listright.html diff --git a/re2o/utils.py b/re2o/utils.py index 7c49ff0a..d41bcf9d 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -213,3 +213,17 @@ def remove_user_room(room, force=True): if force or not user.has_access(): user.room = None user.save() + + +def permission_tree(queryset=None): + r = {} + permissions = queryset or Permission.objects.all() + for p in permissions: + key, app, model = p.natural_key() + name = p.name + if app not in r: + r[app] = {} + if model not in r[app]: + r[app][model] = {} + r[app][model][key] = p + return r diff --git a/templates/base.html b/templates/base.html index ced8061e..95bd3f37 100644 --- a/templates/base.html +++ b/templates/base.html @@ -48,6 +48,8 @@ with this program; if not, write to the Free Software Foundation, Inc., + {% block custom_js %}{% endblock %} + {# Load CSS #} {% bootstrap_css %} diff --git a/users/templates/users/edit_listright.html b/users/templates/users/edit_listright.html new file mode 100644 index 00000000..e8437295 --- /dev/null +++ b/users/templates/users/edit_listright.html @@ -0,0 +1,141 @@ +{% extends 'users/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 Lara 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 static %} +{% load i18n %} +{% block title %}{% trans "Users" %}{% endblock %} + +{% block custom_js %} + + + + +{% endblock %} + +{% block content %} + +{% bootstrap_form_errors form %} + +
+ {% csrf_token %} + {% bootstrap_field form.name %} + {% bootstrap_field form.unix_name %} + {% if form.gid %} + {% bootstrap_field form.gid %} + {% endif %} + {% bootstrap_field form.critical %} + {% bootstrap_field form.details %} + +
+ {% bootstrap_field form.permissions %} +
+ {% bootstrap_button action_name button_type="submit" icon='ok' button_class='btn-success' %} +
+{% if load_js_file %} + +{% endif %} +
+
+
+ + + +{% endblock %} + diff --git a/users/views.py b/users/views.py index f37b9648..8450b9c2 100644 --- a/users/views.py +++ b/users/views.py @@ -61,7 +61,7 @@ from preferences.models import OptionalUser, GeneralOption, AssoOption from importlib import import_module from re2o.settings_local import OPTIONNAL_APPS_RE2O from re2o.views import form -from re2o.utils import all_has_access +from re2o.utils import all_has_access, permission_tree from re2o.base import re2o_paginator, SortTable from re2o.acl import ( can_create, @@ -122,7 +122,9 @@ def new_user(request): GTU_sum_up = GeneralOption.get_cached_value("GTU_sum_up") GTU = GeneralOption.get_cached_value("GTU") - is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") + is_set_password_allowed = OptionalUser.get_cached_value( + "allow_set_password_during_user_creation" + ) if user.is_valid(): user = user.save() @@ -199,11 +201,7 @@ def edit_club_admin_members(request, club_instance, **_kwargs): reverse("users:profil", kwargs={"userid": str(club_instance.id)}) ) return form( - { - "userform": club, - "showCGU": False, - "action_name": _("Edit"), - }, + {"userform": club, "showCGU": False, "action_name": _("Edit"),}, "users/user.html", request, ) @@ -233,9 +231,7 @@ def edit_info(request, user, userid): return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {"userform": user_form, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": user_form, "action_name": _("Edit")}, "users/user.html", request, ) @@ -249,12 +245,12 @@ def state(request, user, userid): user_instance = state_form.save() messages.success(request, _("The states were edited.")) if user_instance.trigger_email_changed_state(request): - messages.success(request, _("An email to confirm the address was sent.")) + messages.success( + request, _("An email to confirm the address was sent.") + ) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {"userform": state_form, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": state_form, "action_name": _("Edit")}, "users/user.html", request, ) @@ -269,9 +265,7 @@ def groups(request, user, userid): messages.success(request, _("The groups were edited.")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - {"userform": group_form, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": group_form, "action_name": _("Edit")}, "users/user.html", request, ) @@ -324,9 +318,7 @@ def new_serviceuser(request): messages.success(request, _("The service user was created.")) return redirect(reverse("users:index-serviceusers")) return form( - {"userform": user, "action_name": _("Add")}, - "users/user.html", - request, + {"userform": user, "action_name": _("Add")}, "users/user.html", request, ) @@ -341,9 +333,7 @@ def edit_serviceuser(request, serviceuser, **_kwargs): messages.success(request, _("The service user was edited.")) return redirect(reverse("users:index-serviceusers")) return form( - {"userform": serviceuser, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": serviceuser, "action_name": _("Edit")}, "users/user.html", request, ) @@ -379,9 +369,7 @@ def add_ban(request, user, userid): return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) if user.is_ban(): messages.error(request, _("Warning: this user already has an active ban.")) - return form( - {"userform": ban, "action_name": _("Add")}, "users/user.html", request - ) + return form({"userform": ban, "action_name": _("Add")}, "users/user.html", request) @login_required @@ -398,9 +386,7 @@ def edit_ban(request, ban_instance, **_kwargs): ban.save() messages.success(request, _("The ban was edited.")) return redirect(reverse("users:index")) - return form( - {"userform": ban, "action_name": _("Edit")}, "users/user.html", request - ) + return form({"userform": ban, "action_name": _("Edit")}, "users/user.html", request) @login_required @@ -433,9 +419,7 @@ def add_whitelist(request, user, userid): request, _("Warning: this user already has an active whitelist.") ) return form( - {"userform": whitelist, "action_name": _("Add")}, - "users/user.html", - request, + {"userform": whitelist, "action_name": _("Add")}, "users/user.html", request, ) @@ -453,9 +437,7 @@ def edit_whitelist(request, whitelist_instance, **_kwargs): messages.success(request, _("The whitelist was edited.")) return redirect(reverse("users:index")) return form( - {"userform": whitelist, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": whitelist, "action_name": _("Edit")}, "users/user.html", request, ) @@ -488,11 +470,7 @@ def add_emailaddress(request, user, userid): messages.success(request, _("The local email account was created.")) return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) return form( - { - "userform": emailaddress, - "showCGU": False, - "action_name": _("Add"), - }, + {"userform": emailaddress, "showCGU": False, "action_name": _("Add"),}, "users/user.html", request, ) @@ -515,11 +493,7 @@ def edit_emailaddress(request, emailaddress_instance, **_kwargs): ) ) return form( - { - "userform": emailaddress, - "showCGU": False, - "action_name": _("Edit"), - }, + {"userform": emailaddress, "showCGU": False, "action_name": _("Edit"),}, "users/user.html", request, ) @@ -555,7 +529,9 @@ def edit_email_settings(request, user_instance, **_kwargs): messages.success(request, _("The email settings were edited.")) if user_instance.send_confirm_email_if_necessary(request): - messages.success(request, _("An email to confirm your address was sent.")) + messages.success( + request, _("An email to confirm your address was sent.") + ) return redirect( reverse("users:profil", kwargs={"userid": str(user_instance.id)}) @@ -583,9 +559,7 @@ def add_school(request): messages.success(request, _("The school was added.")) return redirect(reverse("users:index-school")) return form( - {"userform": school, "action_name": _("Add")}, - "users/user.html", - request, + {"userform": school, "action_name": _("Add")}, "users/user.html", request, ) @@ -601,9 +575,7 @@ def edit_school(request, school_instance, **_kwargs): messages.success(request, _("The school was edited.")) return redirect(reverse("users:index-school")) return form( - {"userform": school, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": school, "action_name": _("Edit")}, "users/user.html", request, ) @@ -661,9 +633,7 @@ def edit_shell(request, shell_instance, **_kwargs): messages.success(request, _("The shell was edited.")) return redirect(reverse("users:index-shell")) return form( - {"userform": shell, "action_name": _("Edit")}, - "users/user.html", - request, + {"userform": shell, "action_name": _("Edit")}, "users/user.html", request, ) @@ -675,7 +645,9 @@ def del_shell(request, shell, **_kwargs): shell.delete() messages.success(request, _("The shell was deleted.")) return redirect(reverse("users:index-shell")) - return form({"objet": shell, "objet_name": _("shell")}, "users/delete.html", request) + return form( + {"objet": shell, "objet_name": _("shell")}, "users/delete.html", request + ) @login_required @@ -689,8 +661,8 @@ def add_listright(request): messages.success(request, _("The group of rights was added.")) return redirect(reverse("users:index-listright")) return form( - {"userform": listright, "action_name": _("Add")}, - "users/user.html", + {"form": listright, "action_name": _("Add"), "permissions": permission_tree()}, + "users/edit_listright.html", request, ) @@ -700,15 +672,20 @@ def add_listright(request): def edit_listright(request, listright_instance, **_kwargs): """ Editer un groupe/droit, necessite droit bureau, à partir du listright id """ - listright = ListRightForm(request.POST or None, instance=listright_instance) - if listright.is_valid(): - if listright.changed_data: - listright.save() + listright_form = ListRightForm(request.POST or None, instance=listright_instance) + if listright_form.is_valid(): + if listright_form.changed_data: + listright_form.save() messages.success(request, _("The group of rights was edited.")) return redirect(reverse("users:index-listright")) return form( - {"userform": listright, "action_name": _("Edit")}, - "users/user.html", + { + "form": listright_form, + "action_name": _("Edit"), + "permissions": permission_tree(), + "instance": listright_instance, + }, + "users/edit_listright.html", request, ) @@ -1063,7 +1040,7 @@ def process_email(request, req): return form( {"email": user.email, "name": user.get_full_name()}, "users/confirm_email.html", - request + request, ) @@ -1084,11 +1061,7 @@ def resend_confirmation_email(request, logged_user, userid): messages.success(request, _("An email to confirm your address was sent.")) return redirect(reverse("users:profil", kwargs={"userid": userid})) - return form( - {"email": user.email}, - "users/resend_confirmation_email.html", - request - ) + return form({"email": user.email}, "users/resend_confirmation_email.html", request) @login_required From 315a527bf2e0c62d22f9bfdf6e26fd9713a7e1cc Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 21 Apr 2020 17:48:42 +0200 Subject: [PATCH 141/490] Remove useless imports --- api/serializers.py | 2 -- api/views.py | 6 ------ topologie/api/urls.py | 3 +-- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/api/serializers.py b/api/serializers.py index 4b0cd4af..921a4ac6 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -24,8 +24,6 @@ from rest_framework import serializers -import preferences.models as preferences -import users.models as users # The namespace used for the API. It must match the namespace used in the # urlpatterns to include the API URLs. diff --git a/api/views.py b/api/views.py index 5ec0db1d..4ab37609 100644 --- a/api/views.py +++ b/api/views.py @@ -36,12 +36,6 @@ from rest_framework.authtoken.models import Token from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.response import Response -# import cotisations.models as cotisations -import machines.models as machines -import preferences.models as preferences -import topologie.models as topologie -import users.models as users -from re2o.utils import all_active_interfaces, all_has_access from . import serializers from .pagination import PageSizedPagination from .permissions import ACLPermission diff --git a/topologie/api/urls.py b/topologie/api/urls.py index c5c93f8f..f6febb95 100644 --- a/topologie/api/urls.py +++ b/topologie/api/urls.py @@ -36,11 +36,10 @@ urls_viewset = [ ] urls_view = [ - # (r"topologie/portprofile", views.PortProfileViewSet) (r"topologie/switchs-ports-config", views.SwitchPortView), (r"topologie/switchs-role", views.RoleView), # Deprecated (r"switchs/ports-config", views.SwitchPortView), (r"switchs/role", views.RoleView), -] \ No newline at end of file +] From 8730c2ecfebcca77f5a1e5942b9e5a61f2bdceed Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 21 Apr 2020 18:08:18 +0200 Subject: [PATCH 142/490] Production version of VueJS --- users/templates/users/edit_listright.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/edit_listright.html b/users/templates/users/edit_listright.html index e8437295..00eb62c8 100644 --- a/users/templates/users/edit_listright.html +++ b/users/templates/users/edit_listright.html @@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block custom_js %} - + {% endblock %} From c2b5a26d8fb41c611f98e109eb6789501ebcc565 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 21 Apr 2020 18:17:47 +0200 Subject: [PATCH 143/490] Fix dead code. --- users/templates/users/edit_listright.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/users/templates/users/edit_listright.html b/users/templates/users/edit_listright.html index 00eb62c8..d07b31bf 100644 --- a/users/templates/users/edit_listright.html +++ b/users/templates/users/edit_listright.html @@ -120,17 +120,13 @@ with this program; if not, write to the Free Software Foundation, Inc., onNodeChecked(node) { if ("checkbox_value" in node.data) { var selector = '#id_ListRight-permissions input[value="'.concat(node.data.checkbox_value, '"]'); - console.log(selector); document.querySelector(selector).checked=true; - // document.getElementById(node.data.checkbox_id).checked=true; } }, onNodeUnchecked(node) { if ("checkbox_value" in node.data) { var selector = '#id_ListRight-permissions input[value="'.concat(node.data.checkbox_value, '"]'); - console.log(selector); document.querySelector(selector).checked=false; - // document.getElementById(node.data.checkbox_id).checked=false; } }, } From 9e1cd17a1b4ac42f64377b45088ac953e3e75063 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 16:47:09 +0200 Subject: [PATCH 144/490] Handle empty emails --- users/forms.py | 60 ++++++++++++++++++++++++++++++++++++++++++------- users/models.py | 2 -- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/users/forms.py b/users/forms.py index 3440f2f9..2f3c2e86 100644 --- a/users/forms.py +++ b/users/forms.py @@ -147,10 +147,15 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( - "email" - ): - return self.cleaned_data.get("email").lower() + new_email = self.cleaned_data.get("email") + + if not new_email or len(new_email) == 0: + raise forms.ValidationError( + _("Email field cannot be empty.") + ) + + if not OptionalUser.objects.first().local_email_domain in new_email: + return new_email.lower() else: raise forms.ValidationError( _("You can't use an internal address as your external address.") @@ -481,6 +486,17 @@ class AdherentCreationForm(AdherentForm): self.fields.pop("password1") self.fields.pop("password2") + def clean_email(self): + """Forbid empty email""" + new_email = self.cleaned_data.get("email") + + if not new_email or len(new_email) == 0: + raise forms.ValidationError( + _("Email field cannot be empty.") + ) + + return new_email + def clean_password2(self): """Verifie que password1 et 2 sont identiques (si nécessaire)""" send_email = self.cleaned_data.get("init_password_by_mail") @@ -520,6 +536,7 @@ class AdherentEditForm(AdherentForm): self.fields["gpg_fingerprint"].widget.attrs["placeholder"] = _( "Leave empty if you don't have any GPG key." ) + self.user = kwargs["instance"] if "shell" in self.fields: self.fields["shell"].empty_label = _("Default shell") @@ -539,6 +556,22 @@ class AdherentEditForm(AdherentForm): "shortcuts_enabled", ] + def clean_email(self): + """Forbid empty email""" + original_email = self.user.email + new_email = self.cleaned_data.get("email") + + # Allow empty emails if the user had an empty email before + if original_email is None or len(original_email) == 0: + return new_email + + if new_email is None or len(new_email) == 0: + raise forms.ValidationError( + _("Email field cannot be empty.") + ) + + return new_email + class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé @@ -832,6 +865,7 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) + self.user = kwargs["instance"] self.fields["email"].label = _("Main email address") if "local_email_redirect" in self.fields: self.fields["local_email_redirect"].label = _("Redirect local emails") @@ -839,10 +873,20 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): self.fields["local_email_enabled"].label = _("Use local emails") def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( - "email" - ): - return self.cleaned_data.get("email").lower() + original_email = self.user.email + new_email = self.cleaned_data.get("email") + + # Allow empty emails if the user had an empty email before + if original_email is None or len(original_email) == 0: + return new_email + + if new_email is None or len(new_email) == 0: + raise forms.ValidationError( + _("Email field cannot be empty.") + ) + + if not OptionalUser.objects.first().local_email_domain in new_email: + return new_email.lower() else: raise forms.ValidationError( _("You can't use a {} address.").format( diff --git a/users/models.py b/users/models.py index 42df3f5a..8958a95d 100755 --- a/users/models.py +++ b/users/models.py @@ -205,8 +205,6 @@ class User( validators=[linux_user_validator], ) email = models.EmailField( - blank=True, - null=True, help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( From b968f23b65583e31af908df858fd6c5d59ae85ca Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 15:01:03 +0000 Subject: [PATCH 145/490] Leave user email blank / null for compatilibity --- users/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/users/models.py b/users/models.py index 8958a95d..42df3f5a 100755 --- a/users/models.py +++ b/users/models.py @@ -205,6 +205,8 @@ class User( validators=[linux_user_validator], ) email = models.EmailField( + blank=True, + null=True, help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( From 77a1b39dbf87c46b400f95dcea71fe14bc32d362 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 17:07:09 +0200 Subject: [PATCH 146/490] Add required for email field when necessary --- users/forms.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/users/forms.py b/users/forms.py index 2f3c2e86..01820286 100644 --- a/users/forms.py +++ b/users/forms.py @@ -145,6 +145,7 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields["email"].required = True def clean_email(self): new_email = self.cleaned_data.get("email") @@ -471,6 +472,7 @@ class AdherentCreationForm(AdherentForm): def __init__(self, *args, **kwargs): super(AdherentCreationForm, self).__init__(*args, **kwargs) gtu_file = GeneralOption.get_cached_value("GTU") + self.fields["email"].required = True self.fields["gtu_check"].label = mark_safe( "%s %s." % ( @@ -537,6 +539,7 @@ class AdherentEditForm(AdherentForm): "Leave empty if you don't have any GPG key." ) self.user = kwargs["instance"] + self.fields["email"].required = self.user.email and len(self.user.email) if "shell" in self.fields: self.fields["shell"].empty_label = _("Default shell") @@ -867,6 +870,7 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) self.user = kwargs["instance"] self.fields["email"].label = _("Main email address") + self.fields["email"].required = self.user.email and len(self.user.email) if "local_email_redirect" in self.fields: self.fields["local_email_redirect"].label = _("Redirect local emails") if "local_email_enabled" in self.fields: From 8befbb8ad82da43d38e6f4d7222abe587551dc07 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 16:47:09 +0200 Subject: [PATCH 147/490] Handle empty emails --- users/models.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/users/models.py b/users/models.py index 42df3f5a..8958a95d 100755 --- a/users/models.py +++ b/users/models.py @@ -205,8 +205,6 @@ class User( validators=[linux_user_validator], ) email = models.EmailField( - blank=True, - null=True, help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( From 259e9eff519b291b9acdd8e7f0f508ac8aa75123 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 15:01:03 +0000 Subject: [PATCH 148/490] Leave user email blank / null for compatilibity --- users/models.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/users/models.py b/users/models.py index 8958a95d..42df3f5a 100755 --- a/users/models.py +++ b/users/models.py @@ -205,6 +205,8 @@ class User( validators=[linux_user_validator], ) email = models.EmailField( + blank=True, + null=True, help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( From 4a7c5c1bdbe931d69a8370764d966e2d5098ef7f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 18:19:03 +0200 Subject: [PATCH 149/490] Make email check more pythonic --- users/forms.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/users/forms.py b/users/forms.py index 01820286..92d46898 100644 --- a/users/forms.py +++ b/users/forms.py @@ -150,7 +150,7 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): def clean_email(self): new_email = self.cleaned_data.get("email") - if not new_email or len(new_email) == 0: + if not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) @@ -492,7 +492,7 @@ class AdherentCreationForm(AdherentForm): """Forbid empty email""" new_email = self.cleaned_data.get("email") - if not new_email or len(new_email) == 0: + if not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) @@ -539,7 +539,7 @@ class AdherentEditForm(AdherentForm): "Leave empty if you don't have any GPG key." ) self.user = kwargs["instance"] - self.fields["email"].required = self.user.email and len(self.user.email) + self.fields["email"].required = bool(self.user.email) if "shell" in self.fields: self.fields["shell"].empty_label = _("Default shell") @@ -565,10 +565,10 @@ class AdherentEditForm(AdherentForm): new_email = self.cleaned_data.get("email") # Allow empty emails if the user had an empty email before - if original_email is None or len(original_email) == 0: + if not original_email: return new_email - if new_email is None or len(new_email) == 0: + if not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) @@ -870,7 +870,7 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): super(EmailSettingsForm, self).__init__(*args, prefix=prefix, **kwargs) self.user = kwargs["instance"] self.fields["email"].label = _("Main email address") - self.fields["email"].required = self.user.email and len(self.user.email) + self.fields["email"].required = bool(self.user.email) if "local_email_redirect" in self.fields: self.fields["local_email_redirect"].label = _("Redirect local emails") if "local_email_enabled" in self.fields: @@ -881,10 +881,10 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): new_email = self.cleaned_data.get("email") # Allow empty emails if the user had an empty email before - if original_email is None or len(original_email) == 0: + if not original_email: return new_email - if new_email is None or len(new_email) == 0: + if not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) From 1755f6e624357c20fd4d4da5945249053ef06155 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 16:31:26 +0000 Subject: [PATCH 150/490] Prevent user email from being null --- users/migrations/0090_auto_20200421_1825.py | 20 ++++++++++++++++++++ users/models.py | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 users/migrations/0090_auto_20200421_1825.py diff --git a/users/migrations/0090_auto_20200421_1825.py b/users/migrations/0090_auto_20200421_1825.py new file mode 100644 index 00000000..3a659d46 --- /dev/null +++ b/users/migrations/0090_auto_20200421_1825.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-21 16:25 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0089_auto_20200418_0112'), + ] + + operations = [ + migrations.AlterField( + model_name='user', + name='email', + field=models.EmailField(blank=True, default='', help_text='External email address allowing us to contact you.', max_length=254), + ), + ] diff --git a/users/models.py b/users/models.py index 42df3f5a..805e8b69 100755 --- a/users/models.py +++ b/users/models.py @@ -206,7 +206,7 @@ class User( ) email = models.EmailField( blank=True, - null=True, + default="", help_text=_("External email address allowing us to contact you."), ) local_email_redirect = models.BooleanField( From 6235909a9ad2082e20d12acc7962fef8964dfca2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 18:40:19 +0200 Subject: [PATCH 151/490] Fix some email checks being bypassed for legacy users --- users/forms.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/users/forms.py b/users/forms.py index 92d46898..34d5bfa9 100644 --- a/users/forms.py +++ b/users/forms.py @@ -564,11 +564,8 @@ class AdherentEditForm(AdherentForm): original_email = self.user.email new_email = self.cleaned_data.get("email") - # Allow empty emails if the user had an empty email before - if not original_email: - return new_email - - if not new_email: + # Allow empty emails only if the user had an empty email before + if original_email and not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) @@ -880,11 +877,8 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): original_email = self.user.email new_email = self.cleaned_data.get("email") - # Allow empty emails if the user had an empty email before - if not original_email: - return new_email - - if not new_email: + # Allow empty emails only if the user had an empty email before + if original_email and not new_email: raise forms.ValidationError( _("Email field cannot be empty.") ) From dc67ed5298316cfe420016c23563a98d2610aec1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 21 Apr 2020 19:27:12 +0200 Subject: [PATCH 152/490] Check email in user clean (factorise code) --- users/forms.py | 70 ------------------------------------------------- users/models.py | 41 +++++++++++++++-------------- 2 files changed, 22 insertions(+), 89 deletions(-) diff --git a/users/forms.py b/users/forms.py index 34d5bfa9..d9d68c88 100644 --- a/users/forms.py +++ b/users/forms.py @@ -147,21 +147,6 @@ class UserCreationForm(FormRevMixin, forms.ModelForm): super(UserCreationForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields["email"].required = True - def clean_email(self): - new_email = self.cleaned_data.get("email") - - if not new_email: - raise forms.ValidationError( - _("Email field cannot be empty.") - ) - - if not OptionalUser.objects.first().local_email_domain in new_email: - return new_email.lower() - else: - raise forms.ValidationError( - _("You can't use an internal address as your external address.") - ) - class Meta: model = Adherent fields = ("pseudo", "surname", "email") @@ -358,18 +343,6 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): label=_("Force the move?"), initial=False, required=False ) - def clean_email(self): - if not OptionalUser.objects.first().local_email_domain in self.cleaned_data.get( - "email" - ): - return self.cleaned_data.get("email").lower() - else: - raise forms.ValidationError( - _("You can't use a {} address.").format( - OptionalUser.objects.first().local_email_domain - ) - ) - def clean_telephone(self): """Verifie que le tel est présent si 'option est validée dans preferences""" @@ -488,17 +461,6 @@ class AdherentCreationForm(AdherentForm): self.fields.pop("password1") self.fields.pop("password2") - def clean_email(self): - """Forbid empty email""" - new_email = self.cleaned_data.get("email") - - if not new_email: - raise forms.ValidationError( - _("Email field cannot be empty.") - ) - - return new_email - def clean_password2(self): """Verifie que password1 et 2 sont identiques (si nécessaire)""" send_email = self.cleaned_data.get("init_password_by_mail") @@ -559,19 +521,6 @@ class AdherentEditForm(AdherentForm): "shortcuts_enabled", ] - def clean_email(self): - """Forbid empty email""" - original_email = self.user.email - new_email = self.cleaned_data.get("email") - - # Allow empty emails only if the user had an empty email before - if original_email and not new_email: - raise forms.ValidationError( - _("Email field cannot be empty.") - ) - - return new_email - class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): """Formulaire de base d'edition d'un user. Formulaire de base, utilisé @@ -873,25 +822,6 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): if "local_email_enabled" in self.fields: self.fields["local_email_enabled"].label = _("Use local emails") - def clean_email(self): - original_email = self.user.email - new_email = self.cleaned_data.get("email") - - # Allow empty emails only if the user had an empty email before - if original_email and not new_email: - raise forms.ValidationError( - _("Email field cannot be empty.") - ) - - if not OptionalUser.objects.first().local_email_domain in new_email: - return new_email.lower() - else: - raise forms.ValidationError( - _("You can't use a {} address.").format( - OptionalUser.objects.first().local_email_domain - ) - ) - class Meta: model = User fields = ["email", "local_email_enabled", "local_email_redirect"] diff --git a/users/models.py b/users/models.py index 805e8b69..86d2cb5f 100755 --- a/users/models.py +++ b/users/models.py @@ -1323,32 +1323,35 @@ class User( self.__original_state = self.state self.__original_email = self.email - def clean(self, *args, **kwargs): - """Check if this pseudo is already used by any mailalias. - Better than raising an error in post-save and catching it""" + def clean_pseudo(self, *args, **kwargs): if EMailAddress.objects.filter(local_part=self.pseudo.lower()).exclude( user_id=self.id ): raise ValidationError(_("This username is already used.")) - if ( - not self.local_email_enabled - and not self.email - and not (self.state == self.STATE_FULL_ARCHIVE) - ): - raise ValidationError( - _( - "There is neither a local email address nor an external" - " email address for this user." - ) - ) - if self.local_email_redirect and not self.email: - raise ValidationError( - _( - "You can't redirect your local emails if no external email" - " address has been set." + + def clean_email(self, *args, **kwargs): + # Allow empty emails if the user had an empty email before + if not self.email and (self.__original_email or not self.pk): + raise forms.ValidationError( + _("Email field cannot be empty.") + ) + + self.email = self.email.lower() + + if OptionalUser.get_cached_value("local_email_domain") in self.email: + raise forms.ValidationError( + _("You can't use a {} address as an external contact address.").format( + OptionalUser.get_cached_value("local_email_domain") ) ) + def clean(self, *args, **kwargs): + super(User, self).clean(*args, **kwargs) + """Check if this pseudo is already used by any mailalias. + Better than raising an error in post-save and catching it""" + self.clean_pseudo(*args, **kwargs) + self.clean_email(*args, **kwargs) + def __str__(self): return self.pseudo From 5eb89e85ba734cc1863b3dad2f1c3a7015015234 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 21:38:31 +0200 Subject: [PATCH 153/490] Improve comments for User clean methods --- users/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/users/models.py b/users/models.py index 86d2cb5f..4d9e1f20 100755 --- a/users/models.py +++ b/users/models.py @@ -1330,8 +1330,9 @@ class User( raise ValidationError(_("This username is already used.")) def clean_email(self, *args, **kwargs): - # Allow empty emails if the user had an empty email before - if not self.email and (self.__original_email or not self.pk): + # Allow empty emails only if the user had an empty email before + is_created = not self.pk + if not self.email and (self.__original_email or is_created): raise forms.ValidationError( _("Email field cannot be empty.") ) @@ -1346,9 +1347,9 @@ class User( ) def clean(self, *args, **kwargs): - super(User, self).clean(*args, **kwargs) """Check if this pseudo is already used by any mailalias. Better than raising an error in post-save and catching it""" + super(User, self).clean(*args, **kwargs) self.clean_pseudo(*args, **kwargs) self.clean_email(*args, **kwargs) From a1d35a2c9bf30f09cc5652d1a5074dab5e5fcd34 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 21 Apr 2020 19:48:59 +0000 Subject: [PATCH 154/490] Add missing translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 149 ++++---- logs/locale/fr/LC_MESSAGES/django.po | 2 +- machines/locale/fr/LC_MESSAGES/django.po | 192 +++++----- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 7 +- re2o/locale/fr/LC_MESSAGES/django.po | 4 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 76 ++-- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 388 ++++++++++---------- 12 files changed, 411 insertions(+), 417 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index fb747f3e..8040325f 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index ec0098c2..ef9b3fa5 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" @@ -37,7 +37,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:687 +#: cotisations/forms.py:73 cotisations/models.py:688 msgid "Member" msgstr "Adhérent" @@ -154,7 +154,7 @@ msgstr "Peut voir un objet facture" msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:145 cotisations/models.py:461 cotisations/views.py:376 +#: cotisations/models.py:145 cotisations/models.py:462 cotisations/views.py:376 #: cotisations/views.py:571 msgid "invoice" msgstr "facture" @@ -217,115 +217,115 @@ msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:377 +#: cotisations/models.py:378 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:379 +#: cotisations/models.py:380 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:380 +#: cotisations/models.py:381 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:381 +#: cotisations/models.py:382 msgid "address" msgstr "adresse" -#: cotisations/models.py:382 +#: cotisations/models.py:383 msgid "paid" msgstr "payé" -#: cotisations/models.py:383 +#: cotisations/models.py:384 msgid "remark" msgstr "remarque" -#: cotisations/models.py:388 +#: cotisations/models.py:389 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:391 +#: cotisations/models.py:392 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:426 +#: cotisations/models.py:427 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:432 +#: cotisations/models.py:433 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:454 cotisations/models.py:693 -#: cotisations/models.py:951 +#: cotisations/models.py:455 cotisations/models.py:694 +#: cotisations/models.py:952 msgid "Connection" msgstr "Connexion" -#: cotisations/models.py:455 cotisations/models.py:694 -#: cotisations/models.py:952 +#: cotisations/models.py:456 cotisations/models.py:695 +#: cotisations/models.py:953 msgid "Membership" msgstr "Adhésion" -#: cotisations/models.py:456 cotisations/models.py:689 -#: cotisations/models.py:695 cotisations/models.py:953 +#: cotisations/models.py:457 cotisations/models.py:690 +#: cotisations/models.py:696 cotisations/models.py:954 msgid "Both of them" msgstr "Les deux" -#: cotisations/models.py:465 +#: cotisations/models.py:466 msgid "amount" msgstr "montant" -#: cotisations/models.py:468 +#: cotisations/models.py:469 msgid "article" msgstr "article" -#: cotisations/models.py:471 +#: cotisations/models.py:472 msgid "price" msgstr "prix" -#: cotisations/models.py:474 cotisations/models.py:707 +#: cotisations/models.py:475 cotisations/models.py:708 msgid "duration (in months)" msgstr "durée (en mois)" -#: cotisations/models.py:480 cotisations/models.py:713 +#: cotisations/models.py:481 cotisations/models.py:714 msgid "duration (in days, will be added to duration in months)" msgstr "durée (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:488 cotisations/models.py:727 -#: cotisations/models.py:964 +#: cotisations/models.py:489 cotisations/models.py:728 +#: cotisations/models.py:965 msgid "subscription type" msgstr "type de cotisation" -#: cotisations/models.py:493 +#: cotisations/models.py:494 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:494 +#: cotisations/models.py:495 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:496 cotisations/models.py:958 +#: cotisations/models.py:497 cotisations/models.py:959 msgid "purchase" msgstr "achat" -#: cotisations/models.py:497 +#: cotisations/models.py:498 msgid "purchases" msgstr "achats" -#: cotisations/models.py:550 cotisations/models.py:747 +#: cotisations/models.py:551 cotisations/models.py:748 msgid "Duration must be specified for a subscription." msgstr "La durée doit être renseignée pour une cotisation." -#: cotisations/models.py:561 +#: cotisations/models.py:562 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:567 +#: cotisations/models.py:568 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:576 +#: cotisations/models.py:577 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -333,15 +333,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:591 +#: cotisations/models.py:592 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:597 +#: cotisations/models.py:598 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:604 +#: cotisations/models.py:605 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -349,134 +349,134 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:620 +#: cotisations/models.py:621 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:688 +#: cotisations/models.py:689 msgid "Club" msgstr "Club" -#: cotisations/models.py:698 +#: cotisations/models.py:699 msgid "designation" msgstr "désignation" -#: cotisations/models.py:701 +#: cotisations/models.py:702 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:719 +#: cotisations/models.py:720 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:730 cotisations/models.py:831 +#: cotisations/models.py:731 cotisations/models.py:832 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:737 +#: cotisations/models.py:738 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:738 +#: cotisations/models.py:739 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:745 +#: cotisations/models.py:746 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:770 +#: cotisations/models.py:771 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:811 +#: cotisations/models.py:812 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:812 +#: cotisations/models.py:813 msgid "bank" msgstr "banque" -#: cotisations/models.py:813 +#: cotisations/models.py:814 msgid "banks" msgstr "banques" -#: cotisations/models.py:829 +#: cotisations/models.py:830 msgid "method" msgstr "moyen" -#: cotisations/models.py:836 +#: cotisations/models.py:837 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:837 +#: cotisations/models.py:838 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:843 +#: cotisations/models.py:844 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:844 +#: cotisations/models.py:845 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:846 +#: cotisations/models.py:847 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:847 +#: cotisations/models.py:848 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:886 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:887 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:896 +#: cotisations/models.py:897 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:916 +#: cotisations/models.py:917 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:935 +#: cotisations/models.py:936 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:966 +#: cotisations/models.py:967 msgid "start date" msgstr "date de début" -#: cotisations/models.py:967 +#: cotisations/models.py:968 msgid "end date" msgstr "date de fin" -#: cotisations/models.py:971 +#: cotisations/models.py:972 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:972 +#: cotisations/models.py:973 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:974 +#: cotisations/models.py:975 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:975 +#: cotisations/models.py:976 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:981 +#: cotisations/models.py:982 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:990 +#: cotisations/models.py:991 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -484,11 +484,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1002 +#: cotisations/models.py:1003 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:1009 +#: cotisations/models.py:1010 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -496,7 +496,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1025 +#: cotisations/models.py:1026 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -933,11 +933,6 @@ msgstr "Créer une facture" msgid "Control the invoices" msgstr "Contrôler les factures" -#: cotisations/utils.py:57 -#, python-format -msgid "Failed to send email: %(error)s." -msgstr "" - #: cotisations/views.py:157 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 22de19e5..88f515fd 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 5ec18685..36fd7335 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -155,7 +155,7 @@ msgstr "Peut voir un objet machine" msgid "Can change the user of a machine" msgstr "Peut changer l'utilisateur d'une machine" -#: machines/models.py:80 machines/views.py:317 +#: machines/models.py:80 machines/views.py:306 msgid "machine" msgstr "machine" @@ -521,7 +521,7 @@ msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" #: machines/models.py:1057 machines/templates/machines/machine.html:144 -#: machines/views.py:485 +#: machines/views.py:474 msgid "SSHFP record" msgstr "enregistrement SSHFP" @@ -537,7 +537,7 @@ msgstr "Peut voir un objet interface" msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: machines/models.py:1096 machines/views.py:369 +#: machines/models.py:1096 machines/views.py:358 msgid "interface" msgstr "interface" @@ -596,7 +596,7 @@ msgstr "Peut voir un objet list d'adresses IPv6" msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: machines/models.py:1423 machines/views.py:429 +#: machines/models.py:1423 machines/views.py:418 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" @@ -951,13 +951,13 @@ msgstr "Afficher le constructeur" msgid "Display the IPv6 address" msgstr "Afficher les adresses IPv6" -#: machines/templates/machines/aff_machines.html:126 machines/views.py:299 -#: machines/views.py:413 machines/views.py:469 machines/views.py:520 -#: machines/views.py:583 machines/views.py:646 machines/views.py:708 -#: machines/views.py:760 machines/views.py:812 machines/views.py:864 -#: machines/views.py:919 machines/views.py:971 machines/views.py:1033 -#: machines/views.py:1089 machines/views.py:1141 machines/views.py:1207 -#: machines/views.py:1259 machines/views.py:1575 +#: machines/templates/machines/aff_machines.html:126 machines/views.py:288 +#: machines/views.py:402 machines/views.py:458 machines/views.py:509 +#: machines/views.py:572 machines/views.py:635 machines/views.py:697 +#: machines/views.py:749 machines/views.py:801 machines/views.py:853 +#: machines/views.py:908 machines/views.py:960 machines/views.py:1022 +#: machines/views.py:1078 machines/views.py:1130 machines/views.py:1196 +#: machines/views.py:1248 machines/views.py:1564 msgid "Edit" msgstr "Modifier" @@ -1432,76 +1432,76 @@ msgstr "Rôles de serveur" msgid "Ports openings" msgstr "Ouvertures de ports" -#: machines/views.py:153 +#: machines/views.py:142 msgid "Select a machine type first." msgstr "Sélectionnez un type de machine d'abord." -#: machines/views.py:244 +#: machines/views.py:233 msgid "The machine was created." msgstr "La machine a été créée." -#: machines/views.py:253 machines/views.py:348 machines/views.py:389 -#: machines/views.py:447 machines/views.py:501 machines/views.py:566 -#: machines/views.py:629 machines/views.py:691 machines/views.py:743 -#: machines/views.py:795 machines/views.py:847 machines/views.py:902 -#: machines/views.py:954 machines/views.py:1011 machines/views.py:1072 -#: machines/views.py:1124 machines/views.py:1190 machines/views.py:1242 +#: machines/views.py:242 machines/views.py:337 machines/views.py:378 +#: machines/views.py:436 machines/views.py:490 machines/views.py:555 +#: machines/views.py:618 machines/views.py:680 machines/views.py:732 +#: machines/views.py:784 machines/views.py:836 machines/views.py:891 +#: machines/views.py:943 machines/views.py:1000 machines/views.py:1061 +#: machines/views.py:1113 machines/views.py:1179 machines/views.py:1231 msgid "Add" msgstr "Ajouter" -#: machines/views.py:285 +#: machines/views.py:274 msgid "The machine was edited." msgstr "La machine a été modifiée." -#: machines/views.py:312 +#: machines/views.py:301 msgid "The machine was deleted." msgstr "La machine a été supprimée." -#: machines/views.py:338 +#: machines/views.py:327 msgid "The interface was created." msgstr "L'interface a été créée." -#: machines/views.py:364 +#: machines/views.py:353 msgid "The interface was deleted." msgstr "L'interface a été supprimée." -#: machines/views.py:384 +#: machines/views.py:373 msgid "The IPv6 addresses list was created." msgstr "La liste d'adresses IPv6 a été créée." -#: machines/views.py:405 +#: machines/views.py:394 msgid "The IPv6 addresses list was edited." msgstr "La liste d'adresses IPv6 a été modifiée." -#: machines/views.py:424 +#: machines/views.py:413 msgid "The IPv6 addresses list was deleted." msgstr "La liste d'adresses IPv6 a été supprimée." -#: machines/views.py:442 +#: machines/views.py:431 msgid "The SSHFP record was created." msgstr "L'enregistrement SSHFP a été créé." -#: machines/views.py:461 +#: machines/views.py:450 msgid "The SSHFP record was edited." msgstr "L'enregistrement SSHFP a été modifié." -#: machines/views.py:480 +#: machines/views.py:469 msgid "The SSHFP record was deleted." msgstr "L'enregistrement SSHFP a été supprimé." -#: machines/views.py:498 +#: machines/views.py:487 msgid "The IP type was created." msgstr "Le type d'IP a été créé." -#: machines/views.py:517 +#: machines/views.py:506 msgid "The IP type was edited." msgstr "Le type d'IP a été modifié." -#: machines/views.py:536 +#: machines/views.py:525 msgid "The IP type was deleted." msgstr "Le type d'IP a été supprimé." -#: machines/views.py:542 +#: machines/views.py:531 #, python-format msgid "" "The IP type %s is assigned to at least one machine, you can't delete it." @@ -1509,27 +1509,27 @@ msgstr "" "Le type d'IP %s est assigné à au moins une machine, vous ne pouvez pas le " "supprimer." -#: machines/views.py:550 machines/views.py:613 machines/views.py:675 -#: machines/views.py:729 machines/views.py:781 machines/views.py:833 -#: machines/views.py:886 machines/views.py:940 machines/views.py:992 -#: machines/views.py:1056 machines/views.py:1110 machines/views.py:1165 -#: machines/views.py:1228 machines/views.py:1280 +#: machines/views.py:539 machines/views.py:602 machines/views.py:664 +#: machines/views.py:718 machines/views.py:770 machines/views.py:822 +#: machines/views.py:875 machines/views.py:929 machines/views.py:981 +#: machines/views.py:1045 machines/views.py:1099 machines/views.py:1154 +#: machines/views.py:1217 machines/views.py:1269 msgid "Delete" msgstr "Supprimer" -#: machines/views.py:563 +#: machines/views.py:552 msgid "The machine type was created." msgstr "Le type de machine a été créé." -#: machines/views.py:580 +#: machines/views.py:569 msgid "The machine type was edited." msgstr "Le type de machine a été modifié." -#: machines/views.py:599 +#: machines/views.py:588 msgid "The machine type was deleted." msgstr "Le type de machine a été supprimé." -#: machines/views.py:605 +#: machines/views.py:594 #, python-format msgid "" "The machine type %s is assigned to at least one machine, you can't delete it." @@ -1537,19 +1537,19 @@ msgstr "" "Le type de machine %s est assigné à au moins un machine, vous ne pouvez pas " "le supprimer." -#: machines/views.py:626 +#: machines/views.py:615 msgid "The extension was created." msgstr "L'extension a été créée." -#: machines/views.py:643 +#: machines/views.py:632 msgid "The extension was edited." msgstr "L'extension a été modifiée." -#: machines/views.py:662 +#: machines/views.py:651 msgid "The extension was deleted." msgstr "L'extension a été supprimée." -#: machines/views.py:668 +#: machines/views.py:657 #, python-format msgid "" "The extension %s is assigned to at least one machine type, you can't delete " @@ -1558,207 +1558,207 @@ msgstr "" "L'extension %s est assignée à au moins un type de machine, vous ne pouvez " "pas le supprimer." -#: machines/views.py:688 +#: machines/views.py:677 msgid "The SOA record was created." msgstr "L'enregistrement SOA a été créé." -#: machines/views.py:705 +#: machines/views.py:694 msgid "The SOA record was edited." msgstr "L'enregistrement SOA a été modifié." -#: machines/views.py:722 +#: machines/views.py:711 msgid "The SOA record was deleted." msgstr "L'enregistrement SOA a été supprimé." -#: machines/views.py:725 +#: machines/views.py:714 #, python-format msgid "Error: the SOA record %s can't be deleted." msgstr "Erreur : l'enregistrement SOA %s ne peut pas être supprimé." -#: machines/views.py:740 +#: machines/views.py:729 msgid "The MX record was created." msgstr "L'enregistrement MX a été créé." -#: machines/views.py:757 +#: machines/views.py:746 msgid "The MX record was edited." msgstr "L'enregistrement MX a été modifié." -#: machines/views.py:774 +#: machines/views.py:763 msgid "The MX record was deleted." msgstr "L'enregistrement MX a été supprimé." -#: machines/views.py:777 +#: machines/views.py:766 #, python-format msgid "Error: the MX record %s can't be deleted." msgstr "Erreur : l'enregistrement MX %s ne peut pas être supprimé." -#: machines/views.py:792 +#: machines/views.py:781 msgid "The NS record was created." msgstr "L'enregistrement NS a été créé." -#: machines/views.py:809 +#: machines/views.py:798 msgid "The NS record was edited." msgstr "L'enregistrement NS a été modifié." -#: machines/views.py:826 +#: machines/views.py:815 msgid "The NS record was deleted." msgstr "L'enregistrement NS a été supprimé." -#: machines/views.py:829 +#: machines/views.py:818 #, python-format msgid "Error: the NS record %s can't be deleted." msgstr "Erreur : l'enregistrement NS %s ne peut pas être supprimé." -#: machines/views.py:844 +#: machines/views.py:833 msgid "The DNAME record was created." msgstr "L'enregistrement DNAME a été créé." -#: machines/views.py:861 +#: machines/views.py:850 msgid "The DNAME record was edited." msgstr "L'enregistrement DNAME a été modifié." -#: machines/views.py:878 +#: machines/views.py:867 msgid "The DNAME record was deleted." msgstr "L'enregistrement DNAME a été supprimé." -#: machines/views.py:882 +#: machines/views.py:871 #, python-format msgid "Error: the DNAME record %s can't be deleted." msgstr "Erreur : l'enregistrement DNAME %s ne peut pas être supprimé." -#: machines/views.py:899 +#: machines/views.py:888 msgid "The TXT record was created." msgstr "L'enregistrement TXT a été créé." -#: machines/views.py:916 +#: machines/views.py:905 msgid "The TXT record was edited." msgstr "L'enregistrement TXT a été modifié." -#: machines/views.py:933 +#: machines/views.py:922 msgid "The TXT record was deleted." msgstr "L'enregistrement TXT a été supprimé." -#: machines/views.py:936 +#: machines/views.py:925 #, python-format msgid "Error: the TXT record %s can't be deleted." msgstr "Erreur : l'enregistrement %s ne peut pas être supprimé." -#: machines/views.py:951 +#: machines/views.py:940 msgid "The SRV record was created." msgstr "L'enregistrement SRV a été créé." -#: machines/views.py:968 +#: machines/views.py:957 msgid "The SRV record was edited." msgstr "L'enregistrement SRV a été modifié." -#: machines/views.py:985 +#: machines/views.py:974 msgid "The SRV record was deleted." msgstr "L'enregistrement SRV a été supprimé." -#: machines/views.py:988 +#: machines/views.py:977 #, python-format msgid "Error: the SRV record %s can't be deleted." msgstr "Erreur : l'enregistrement SRV %s ne peut pas être supprimé." -#: machines/views.py:1006 +#: machines/views.py:995 msgid "The alias was created." msgstr "L'alias a été créé." -#: machines/views.py:1025 +#: machines/views.py:1014 msgid "The alias was edited." msgstr "L'alias a été modifié." -#: machines/views.py:1047 +#: machines/views.py:1036 #, python-format msgid "The alias %s was deleted." msgstr "L'alias %s a été supprimé." -#: machines/views.py:1050 +#: machines/views.py:1039 #, python-format msgid "Error: the alias %s can't be deleted." msgstr "Erreur : l'alias %s ne peut pas être supprimé." -#: machines/views.py:1069 +#: machines/views.py:1058 msgid "The role was created." msgstr "Le rôle a été créé." -#: machines/views.py:1086 +#: machines/views.py:1075 msgid "The role was edited." msgstr "Le rôle a été modifié." -#: machines/views.py:1103 +#: machines/views.py:1092 msgid "The role was deleted." msgstr "Le rôle a été supprimé." -#: machines/views.py:1106 +#: machines/views.py:1095 #, python-format msgid "Error: the role %s can't be deleted." msgstr "Erreur : le rôle %s ne peut pas être supprimé." -#: machines/views.py:1121 +#: machines/views.py:1110 msgid "The service was created." msgstr "Le service a été créé." -#: machines/views.py:1138 +#: machines/views.py:1127 msgid "The service was edited." msgstr "Le service a été modifié." -#: machines/views.py:1157 +#: machines/views.py:1146 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: machines/views.py:1161 +#: machines/views.py:1150 #, python-format msgid "Error: the service %s can't be deleted." msgstr "Erreur : le service %s ne peut pas être supprimé." -#: machines/views.py:1187 +#: machines/views.py:1176 msgid "The VLAN was created." msgstr "Le VLAN a été créé." -#: machines/views.py:1204 +#: machines/views.py:1193 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: machines/views.py:1221 +#: machines/views.py:1210 msgid "The VLAN was deleted." msgstr "Le VLAN a été supprimé." -#: machines/views.py:1224 +#: machines/views.py:1213 #, python-format msgid "Error: the VLAN %s can't be deleted." msgstr "Erreur : le VLAN %s ne peut pas être supprimé." -#: machines/views.py:1239 +#: machines/views.py:1228 msgid "The NAS device was created." msgstr "Le dispositif NAS a été créé." -#: machines/views.py:1256 +#: machines/views.py:1245 msgid "The NAS device was edited." msgstr "Le dispositif NAS a été modifié." -#: machines/views.py:1273 +#: machines/views.py:1262 msgid "The NAS device was deleted." msgstr "Le dispositif NAS a été supprimé." -#: machines/views.py:1276 +#: machines/views.py:1265 #, python-format msgid "Error: the NAS device %s can't be deleted." msgstr "Erreur : le dispositif NAS %s ne peut pas être supprimé." -#: machines/views.py:1502 +#: machines/views.py:1491 msgid "The ports list was edited." msgstr "La liste de ports a été modifiée." -#: machines/views.py:1516 +#: machines/views.py:1505 msgid "The ports list was deleted." msgstr "La liste de ports a été supprimée." -#: machines/views.py:1541 +#: machines/views.py:1530 msgid "The ports list was created." msgstr "La liste de ports a été créée." -#: machines/views.py:1561 +#: machines/views.py:1550 msgid "" "Warning: the IP address is not public, the opening won't have any effect in " "v4." @@ -1766,6 +1766,6 @@ msgstr "" "Attention : l'adresse IP n'est pas publique, l'ouverture n'aura pas d'effet " "en v4." -#: machines/views.py:1572 +#: machines/views.py:1561 msgid "The ports configuration was edited." msgstr "La configuration de ports a été modifiée." diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 31245d63..e60cb464 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 84d7b67c..e90c1e2a 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -1110,10 +1110,9 @@ msgstr "" "compte" #: preferences/templates/preferences/display_preferences.html:136 -#, fuzzy, python-format -#| msgid "%(delete_notyetactive)s days" +#, python-format msgid "%(disable_emailnotyetconfirmed)s days" -msgstr "%(delete_notyetactive)s jours" +msgstr "%(disable_emailnotyetconfirmed)s jours" #: preferences/templates/preferences/display_preferences.html:140 msgid "Users general permissions" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 1c408790..84b4bd34 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -74,7 +74,7 @@ msgstr "Format : {main} {more}" msgid "Format: {main}" msgstr "Format : {main}" -#: re2o/mail_utils.py:42 +#: re2o/mail_utils.py:42 re2o/mail_utils.py:56 #, python-format msgid "Failed to send email: %(error)s." msgstr "Échec de l'envoi du mail : %(error)s." diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index bf54fb7d..2e18a0a2 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 3935e92a..ad8e9968 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,11 +30,11 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: templates/admin/base_site.html:65 templates/base.html:288 +#: templates/admin/base_site.html:65 templates/base.html:290 msgid "powered by" msgstr "propulsé par" -#: templates/admin/base_site.html:69 templates/base.html:296 +#: templates/admin/base_site.html:69 templates/base.html:298 msgid "" "This software is under the terms of the GPLv2 License." @@ -80,7 +80,7 @@ msgstr "" msgid "Unknown content" msgstr "" -#: templates/base.html:70 templates/registration/logged_out.html:11 +#: templates/base.html:72 templates/registration/logged_out.html:11 #: templates/registration/password_change_done.html:11 #: templates/registration/password_change_form.html:11 #: templates/registration/password_reset_complete.html:11 @@ -90,145 +90,145 @@ msgstr "" msgid "Home" msgstr "" -#: templates/base.html:91 +#: templates/base.html:93 msgid "Users" msgstr "Utilisateurs" -#: templates/base.html:94 +#: templates/base.html:96 msgid "Manage the users" msgstr "Gérer les utilisateurs" -#: templates/base.html:95 +#: templates/base.html:97 msgid "Manage the clubs" msgstr "Gérer les clubs" -#: templates/base.html:98 +#: templates/base.html:100 msgid "Manage the machines" msgstr "Gérer les machines" -#: templates/base.html:101 +#: templates/base.html:103 msgid "Manage the subscriptions" msgstr "Gérer les cotisations" -#: templates/base.html:114 +#: templates/base.html:116 msgid "Topology" msgstr "Topologie" -#: templates/base.html:116 +#: templates/base.html:118 msgid "Switches" msgstr "Commutateurs réseau" -#: templates/base.html:117 +#: templates/base.html:119 msgid "Access points" msgstr "Points d'accès sans fil" -#: templates/base.html:118 +#: templates/base.html:120 msgid "Rooms" msgstr "Chambres" -#: templates/base.html:128 +#: templates/base.html:130 msgid "Statistics" msgstr "Statistiques" -#: templates/base.html:133 +#: templates/base.html:135 msgid "Administration" msgstr "" -#: templates/base.html:140 +#: templates/base.html:142 msgid "Information and contact" msgstr "Informations et contact" -#: templates/base.html:142 +#: templates/base.html:144 msgid "About" msgstr "À propos" -#: templates/base.html:143 +#: templates/base.html:145 msgid "Contact" msgstr "Contact" -#: templates/base.html:157 +#: templates/base.html:159 msgid "Sign up" msgstr "S'inscrire" -#: templates/base.html:163 templates/registration/login.html:29 +#: templates/base.html:165 templates/registration/login.html:29 #: templates/registration/login.html:36 msgid "Log in" msgstr "" -#: templates/base.html:171 +#: templates/base.html:173 msgid "Search" msgstr "" -#: templates/base.html:185 +#: templates/base.html:187 msgid "My profile" msgstr "Mon profil" -#: templates/base.html:186 +#: templates/base.html:188 msgid "Log out" msgstr "" -#: templates/base.html:221 +#: templates/base.html:223 msgid "Username" msgstr "Pseudo" -#: templates/base.html:225 +#: templates/base.html:227 msgid "Room" msgstr "Chambre" -#: templates/base.html:229 +#: templates/base.html:231 msgid "Internet access" msgstr "Accès Internet" -#: templates/base.html:232 +#: templates/base.html:234 #, python-format msgid "Until %(end_access_date)s" msgstr "Jusqu'au %(end_access_date)s" -#: templates/base.html:234 +#: templates/base.html:236 msgid "Disabled" msgstr "Désactivé" -#: templates/base.html:239 +#: templates/base.html:241 msgid "Membership" msgstr "Adhésion" -#: templates/base.html:242 +#: templates/base.html:244 #, python-format msgid "Until %(end_adhesion_date)s" msgstr "Jusqu'au %(end_adhesion_date)s" -#: templates/base.html:244 +#: templates/base.html:246 msgid "Non member" msgstr "Non adhérent" -#: templates/base.html:252 +#: templates/base.html:254 msgid "View my profile" msgstr "Voir mon profil" -#: templates/base.html:257 +#: templates/base.html:259 msgid "You are not logged in." msgstr "Vous n'êtes pas connecté." -#: templates/base.html:264 +#: templates/base.html:266 #, python-format msgid "%(nb)s active machine" msgid_plural "%(nb)s active machines" msgstr[0] "%(nb)s machine active" msgstr[1] "%(nb)s machines actives" -#: templates/base.html:273 +#: templates/base.html:275 msgid "View my machines" msgstr "Voir mes machines" -#: templates/base.html:286 +#: templates/base.html:288 msgid "Back to top" msgstr "Retour en haut" -#: templates/base.html:290 +#: templates/base.html:292 msgid "Brought to you with ." msgstr "Codé avec ." -#: templates/base.html:293 +#: templates/base.html:295 msgid "About this website" msgstr "À propos de ce site" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 166576ba..df651ec7 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 37a56a9d..b5f45bff 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 35774bd3..edae2c02 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-19 20:16+0200\n" +"POT-Creation-Date: 2020-04-21 21:38+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,15 +35,15 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: users/forms.py:80 +#: users/forms.py:81 msgid "Current password" msgstr "Mot de passe actuel" -#: users/forms.py:83 users/forms.py:628 users/forms.py:656 +#: users/forms.py:84 users/forms.py:598 users/forms.py:626 msgid "New password" msgstr "Nouveau mot de passe" -#: users/forms.py:89 +#: users/forms.py:90 msgid "New password confirmation" msgstr "Confirmation du nouveau mot de passe" @@ -51,38 +51,33 @@ msgstr "Confirmation du nouveau mot de passe" msgid "The new passwords don't match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: users/forms.py:111 +#: users/forms.py:112 msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: users/forms.py:132 users/forms.py:189 users/forms.py:425 -#: users/models.py:1908 +#: users/forms.py:133 users/forms.py:181 users/forms.py:405 +#: users/models.py:1912 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:138 users/forms.py:192 users/forms.py:432 +#: users/forms.py:139 users/forms.py:184 users/forms.py:412 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: users/forms.py:143 users/forms.py:232 +#: users/forms.py:143 users/forms.py:224 msgid "Is admin" msgstr "Est admin" -#: users/forms.py:156 -msgid "You can't use an internal address as your external address." -msgstr "" -"Vous ne pouvez pas utiliser une adresse interne pour votre adresse externe." - -#: users/forms.py:169 users/forms.py:212 users/forms.py:506 +#: users/forms.py:160 users/forms.py:204 users/forms.py:474 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: users/forms.py:241 +#: users/forms.py:233 #, python-format msgid "User is admin: %s" msgstr "L'utilisateur est admin : %s" -#: users/forms.py:287 users/templates/users/aff_clubs.html:38 +#: users/forms.py:279 users/templates/users/aff_clubs.html:38 #: users/templates/users/aff_listright.html:63 #: users/templates/users/aff_listright.html:168 #: users/templates/users/aff_users.html:39 @@ -91,75 +86,71 @@ msgstr "L'utilisateur est admin : %s" msgid "Username" msgstr "Pseudo" -#: users/forms.py:299 +#: users/forms.py:291 msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" msgstr "" "Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" -#: users/forms.py:312 +#: users/forms.py:304 msgid "Impossible to archive users whose end access date is in the future." msgstr "" "Impossible d'archiver des utilisateurs dont la date de fin de connexion est " "dans le futur." -#: users/forms.py:327 users/templates/users/aff_users.html:35 +#: users/forms.py:319 users/templates/users/aff_users.html:35 #: users/templates/users/profil.html:193 users/templates/users/profil.html:367 #: users/templates/users/profil.html:386 msgid "First name" msgstr "Prénom" -#: users/forms.py:328 users/templates/users/aff_users.html:37 +#: users/forms.py:320 users/templates/users/aff_users.html:37 #: users/templates/users/profil.html:199 users/templates/users/profil.html:366 #: users/templates/users/profil.html:385 msgid "Surname" msgstr "Nom" -#: users/forms.py:329 users/forms.py:566 users/models.py:1908 +#: users/forms.py:321 users/forms.py:536 users/models.py:1912 #: users/templates/users/aff_emailaddress.html:36 #: users/templates/users/profil.html:209 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:330 users/forms.py:564 users/forms.py:709 +#: users/forms.py:322 users/forms.py:534 users/forms.py:679 #: users/templates/users/aff_schools.html:37 #: users/templates/users/profil.html:230 msgid "School" msgstr "Établissement" -#: users/forms.py:331 users/forms.py:565 +#: users/forms.py:323 users/forms.py:535 #: users/templates/users/aff_serviceusers.html:34 #: users/templates/users/profil.html:235 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:333 users/forms.py:568 +#: users/forms.py:325 users/forms.py:538 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 #: users/templates/users/profil.html:214 msgid "Room" msgstr "Chambre" -#: users/forms.py:334 users/forms.py:569 +#: users/forms.py:326 users/forms.py:539 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:335 users/forms.py:570 +#: users/forms.py:327 users/forms.py:540 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: users/forms.py:351 +#: users/forms.py:343 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:361 users/forms.py:860 -msgid "You can't use a {} address." -msgstr "Vous ne pouvez pas utiliser une adresse {}." - -#: users/forms.py:371 users/forms.py:593 +#: users/forms.py:351 users/forms.py:563 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:405 +#: users/forms.py:385 msgid "" "If this options is set, you will receive a link to set your initial password " "by email. If you do not have any means of accessing your emails, you can " @@ -174,11 +165,11 @@ msgstr "" "votre adresse dans les délais impartis, votre connexion sera automatiquement " "suspendue." -#: users/forms.py:419 +#: users/forms.py:399 msgid "Send password reset link by email." msgstr "Envoyer le lien de modification du mot de passe par mail." -#: users/forms.py:440 +#: users/forms.py:419 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -189,101 +180,97 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:447 +#: users/forms.py:426 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:472 +#: users/forms.py:452 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:474 +#: users/forms.py:454 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:492 -msgid "Password must contain at least 8 characters." -msgstr "Le mot de passe doit contenir au moins 8 caractères." - -#: users/forms.py:533 +#: users/forms.py:501 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: users/forms.py:536 +#: users/forms.py:506 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: users/forms.py:563 users/templates/users/aff_clubs.html:36 +#: users/forms.py:533 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:571 +#: users/forms.py:541 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:677 users/templates/users/profil.html:277 +#: users/forms.py:647 users/templates/users/profil.html:277 msgid "State" msgstr "État" -#: users/forms.py:678 +#: users/forms.py:648 msgid "Email state" msgstr "État du mail" -#: users/forms.py:696 users/templates/users/aff_listright.html:38 +#: users/forms.py:666 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:722 +#: users/forms.py:692 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:742 +#: users/forms.py:712 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:754 +#: users/forms.py:724 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:763 +#: users/forms.py:733 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:781 +#: users/forms.py:751 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:800 users/forms.py:815 users/templates/users/aff_bans.html:41 +#: users/forms.py:770 users/forms.py:785 users/templates/users/aff_bans.html:41 #: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: users/forms.py:830 +#: users/forms.py:800 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:831 +#: users/forms.py:801 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:847 +#: users/forms.py:818 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:849 +#: users/forms.py:821 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:851 +#: users/forms.py:823 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:903 +#: users/forms.py:863 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:908 +#: users/forms.py:868 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" @@ -336,7 +323,7 @@ msgstr "Non confirmé" msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:204 users/models.py:1561 +#: users/models.py:204 users/models.py:1565 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." @@ -487,7 +474,7 @@ msgstr "Vous n'avez pas le droit de voir ce club." msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1292 users/models.py:1497 +#: users/models.py:1292 users/models.py:1501 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." @@ -495,215 +482,208 @@ msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1332 +#: users/models.py:1330 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1340 -msgid "" -"There is neither a local email address nor an external email address for " -"this user." -msgstr "" -"Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " -"utilisateur." +#: users/models.py:1337 +msgid "Email field cannot be empty." +msgstr "Le champ mail ne peut pas ^êêtre vide" -#: users/models.py:1347 -msgid "" -"You can't redirect your local emails if no external email address has been " -"set." +#: users/models.py:1344 +msgid "You can't use a {} address as an external contact address." msgstr "" -"Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail externe " -"n'a été définie." +"Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." -#: users/models.py:1367 +#: users/models.py:1371 msgid "member" msgstr "adhérent" -#: users/models.py:1368 +#: users/models.py:1372 msgid "members" msgstr "adhérents" -#: users/models.py:1385 +#: users/models.py:1389 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1410 +#: users/models.py:1414 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1420 +#: users/models.py:1424 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1450 +#: users/models.py:1454 msgid "club" msgstr "club" -#: users/models.py:1451 +#: users/models.py:1455 msgid "clubs" msgstr "clubs" -#: users/models.py:1462 +#: users/models.py:1466 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1470 +#: users/models.py:1474 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1565 +#: users/models.py:1569 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1571 +#: users/models.py:1575 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1572 users/views.py:360 +#: users/models.py:1576 users/views.py:349 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1573 +#: users/models.py:1577 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1577 +#: users/models.py:1581 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1644 +#: users/models.py:1648 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1645 +#: users/models.py:1649 msgid "school" msgstr "établissement" -#: users/models.py:1646 +#: users/models.py:1650 msgid "schools" msgstr "établissements" -#: users/models.py:1665 +#: users/models.py:1669 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1671 +#: users/models.py:1675 msgid "Description." msgstr "Description." -#: users/models.py:1674 +#: users/models.py:1678 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1675 +#: users/models.py:1679 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1676 +#: users/models.py:1680 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1721 +#: users/models.py:1725 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1722 users/views.py:679 +#: users/models.py:1726 users/views.py:649 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1723 +#: users/models.py:1727 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1741 +#: users/models.py:1745 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1742 +#: users/models.py:1746 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1743 +#: users/models.py:1747 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1754 +#: users/models.py:1758 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1755 users/views.py:415 +#: users/models.py:1759 users/views.py:400 msgid "ban" msgstr "bannissement" -#: users/models.py:1756 +#: users/models.py:1760 msgid "bans" msgstr "bannissements" -#: users/models.py:1793 +#: users/models.py:1797 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1841 +#: users/models.py:1845 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1842 +#: users/models.py:1846 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1843 +#: users/models.py:1847 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1863 +#: users/models.py:1867 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2061 +#: users/models.py:2065 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2064 +#: users/models.py:2068 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2069 +#: users/models.py:2073 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2071 +#: users/models.py:2075 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2072 +#: users/models.py:2076 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2100 users/models.py:2135 users/models.py:2169 -#: users/models.py:2203 +#: users/models.py:2104 users/models.py:2139 users/models.py:2173 +#: users/models.py:2207 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2105 +#: users/models.py:2109 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2115 +#: users/models.py:2119 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2141 +#: users/models.py:2145 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2161 +#: users/models.py:2165 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -711,13 +691,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2175 +#: users/models.py:2179 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2195 +#: users/models.py:2199 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -725,13 +705,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2209 +#: users/models.py:2213 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2218 +#: users/models.py:2222 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." @@ -840,6 +820,7 @@ msgstr[0] "Total: %(perm_count)s permission" msgstr[1] "Total: %(perm_count)s permissions" #: users/templates/users/aff_listright.html:150 +#: users/templates/users/edit_listright.html:29 #: users/templates/users/index.html:29 users/templates/users/index.html:32 #: users/templates/users/index_ban.html:29 #: users/templates/users/index_clubs.html:29 @@ -888,8 +869,8 @@ msgstr "pour %(name)s." #: users/templates/users/confirm_email.html:39 #: users/templates/users/delete.html:36 #: users/templates/users/mass_archive.html:36 -#: users/templates/users/resend_confirmation_email.html:35 users/views.py:636 -#: users/views.py:740 +#: users/templates/users/resend_confirmation_email.html:35 users/views.py:607 +#: users/views.py:716 msgid "Confirm" msgstr "Confirmer" @@ -1086,14 +1067,14 @@ msgstr "Pas de machine" msgid "Detailed information" msgstr "Informations détaillées" -#: users/templates/users/profil.html:161 users/views.py:206 users/views.py:237 -#: users/views.py:256 users/views.py:273 users/views.py:345 users/views.py:403 -#: users/views.py:457 users/views.py:522 users/views.py:569 users/views.py:605 -#: users/views.py:665 users/views.py:711 +#: users/templates/users/profil.html:161 users/views.py:204 users/views.py:234 +#: users/views.py:253 users/views.py:268 users/views.py:336 users/views.py:389 +#: users/views.py:440 users/views.py:496 users/views.py:544 users/views.py:578 +#: users/views.py:636 users/views.py:684 msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:165 users/views.py:292 users/views.py:1043 +#: users/templates/users/profil.html:165 users/views.py:285 users/views.py:1019 msgid "Change the password" msgstr "Changer le mot de passe" @@ -1333,160 +1314,160 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: users/views.py:135 +#: users/views.py:136 #, python-format msgid "The user %s was created, a confirmation email was sent." msgstr "L'utilisateur %s a été créé, un mail de confirmation a été envoyé." -#: users/views.py:142 +#: users/views.py:143 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: users/views.py:155 +#: users/views.py:156 msgid "Commit" msgstr "Valider" -#: users/views.py:178 +#: users/views.py:179 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: users/views.py:183 +#: users/views.py:184 msgid "Create a club" msgstr "Créer un club" -#: users/views.py:198 +#: users/views.py:199 msgid "The club was edited." msgstr "Le club a été modifié." -#: users/views.py:230 +#: users/views.py:227 msgid "The user was edited." msgstr "L'utilisateur a été modifié." -#: users/views.py:233 +#: users/views.py:230 msgid "Sent a new confirmation email." msgstr "Un nouveau mail de confirmation a été envoyé." -#: users/views.py:251 +#: users/views.py:246 msgid "The states were edited." msgstr "Les états ont été modifié." -#: users/views.py:253 +#: users/views.py:249 msgid "An email to confirm the address was sent." msgstr "Un mail pour confirmer l'adresse a été envoyé." -#: users/views.py:270 +#: users/views.py:265 msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: users/views.py:289 users/views.py:1040 +#: users/views.py:282 users/views.py:1016 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: users/views.py:304 +#: users/views.py:297 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: users/views.py:314 +#: users/views.py:307 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: users/views.py:325 +#: users/views.py:318 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: users/views.py:328 users/views.py:384 users/views.py:437 users/views.py:495 -#: users/views.py:587 users/views.py:650 users/views.py:693 +#: users/views.py:321 users/views.py:372 users/views.py:422 users/views.py:473 +#: users/views.py:562 users/views.py:621 users/views.py:664 msgid "Add" msgstr "Ajouter" -#: users/views.py:342 +#: users/views.py:333 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: users/views.py:357 +#: users/views.py:346 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: users/views.py:379 +#: users/views.py:368 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: users/views.py:382 +#: users/views.py:371 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: users/views.py:400 +#: users/views.py:387 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: users/views.py:413 +#: users/views.py:398 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: users/views.py:430 +#: users/views.py:415 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:434 +#: users/views.py:419 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: users/views.py:454 +#: users/views.py:437 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:469 +#: users/views.py:450 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: users/views.py:474 +#: users/views.py:455 msgid "whitelist" msgstr "accès gracieux" -#: users/views.py:489 +#: users/views.py:470 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: users/views.py:512 +#: users/views.py:489 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: users/views.py:535 +#: users/views.py:508 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: users/views.py:540 +#: users/views.py:513 msgid "email address" msgstr "adresse mail" -#: users/views.py:556 +#: users/views.py:529 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:559 users/views.py:1085 +#: users/views.py:533 users/views.py:1061 msgid "An email to confirm your address was sent." msgstr "Un mail pour confirmer votre adresse a été envoyé." -#: users/views.py:584 +#: users/views.py:559 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: users/views.py:602 +#: users/views.py:575 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: users/views.py:624 +#: users/views.py:595 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: users/views.py:629 +#: users/views.py:600 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1494,31 +1475,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: users/views.py:647 +#: users/views.py:618 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: users/views.py:662 +#: users/views.py:633 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: users/views.py:677 +#: users/views.py:646 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: users/views.py:690 +#: users/views.py:661 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: users/views.py:708 +#: users/views.py:679 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: users/views.py:728 +#: users/views.py:704 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: users/views.py:733 +#: users/views.py:709 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1527,37 +1508,37 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: users/views.py:769 +#: users/views.py:745 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:998 users/views.py:1081 +#: users/views.py:974 users/views.py:1057 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: users/views.py:1000 users/views.py:1008 +#: users/views.py:976 users/views.py:984 msgid "Reset" msgstr "Réinitialiser" -#: users/views.py:1005 +#: users/views.py:981 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: users/views.py:1023 +#: users/views.py:999 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1061 +#: users/views.py:1037 #, python-format msgid "The %s address was confirmed." msgstr "L'adresse mail %s a été confirmée." -#: users/views.py:1108 +#: users/views.py:1080 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1120 +#: users/views.py:1092 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1565,10 +1546,6 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." -#: users/views.py:1160 users/views.py:1184 users/views.py:1199 -msgid "The mailing list doesn't exist." -msgstr "La liste de diffusion n'existe pas." - #: users/widgets.py:39 msgid "Today" msgstr "Aujourd'hui" @@ -1584,3 +1561,26 @@ msgstr "Précédent" #: users/widgets.py:61 msgid "Wk" msgstr "Sem" + +#~ msgid "You can't use a {} address." +#~ msgstr "Vous ne pouvez pas utiliser une adresse {}." + +#~ msgid "Password must contain at least 8 characters." +#~ msgstr "Le mot de passe doit contenir au moins 8 caractères." + +#~ msgid "" +#~ "There is neither a local email address nor an external email address for " +#~ "this user." +#~ msgstr "" +#~ "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " +#~ "utilisateur." + +#~ msgid "" +#~ "You can't redirect your local emails if no external email address has " +#~ "been set." +#~ msgstr "" +#~ "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail " +#~ "externe n'a été définie." + +#~ msgid "The mailing list doesn't exist." +#~ msgstr "La liste de diffusion n'existe pas." From 64fd1835e04936a4d68084bb0e819b9cf2f3d674 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Tue, 21 Apr 2020 22:30:06 +0200 Subject: [PATCH 155/490] Fix #132, return in clean if ip domaine are invalid --- machines/locale/fr/LC_MESSAGES/django.po | 276 ++++++++++++----------- machines/models.py | 2 + 2 files changed, 142 insertions(+), 136 deletions(-) diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 36fd7335..e5f8dfc9 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-21 22:29+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -175,7 +175,7 @@ msgstr "Vous n'avez pas le droit de voir toutes les machines." msgid "Nonexistent user." msgstr "Utilisateur inexistant." -#: machines/models.py:154 machines/models.py:1303 +#: machines/models.py:154 machines/models.py:1305 msgid "You don't have the right to add a machine." msgstr "Vous n'avez pas le droit d'ajouter une machine." @@ -183,7 +183,7 @@ msgstr "Vous n'avez pas le droit d'ajouter une machine." msgid "You don't have the right to add a machine to another user." msgstr "Vous n'avez pas le droit d'ajouter une machine à un autre utilisateur." -#: machines/models.py:168 machines/models.py:1320 +#: machines/models.py:168 machines/models.py:1322 #, python-format msgid "" "You reached the maximum number of interfaces that you are allowed to create " @@ -192,8 +192,8 @@ msgstr "" "Vous avez atteint le nombre maximal d'interfaces que vous pouvez créer vous-" "même (%s)." -#: machines/models.py:189 machines/models.py:1352 machines/models.py:1371 -#: machines/models.py:1474 machines/models.py:1492 +#: machines/models.py:189 machines/models.py:1354 machines/models.py:1373 +#: machines/models.py:1476 machines/models.py:1494 msgid "You don't have the right to edit a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier une machine d'un autre utilisateur." @@ -203,7 +203,7 @@ msgid "You don't have the right to delete a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer une machine d'une autre utilisateur." -#: machines/models.py:228 machines/models.py:1762 +#: machines/models.py:228 machines/models.py:1764 msgid "You don't have the right to view other machines than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." @@ -273,20 +273,24 @@ msgstr "" "supprimer la plage." #: machines/models.py:546 +msgid "Domaine IPv4 start and stop must be valid" +msgstr "Les valeurs IPv4 Domaine ip start et stop doivent être valides" + +#: machines/models.py:548 msgid "Range end must be after range start..." msgstr "La fin de la plage doit être après le début..." -#: machines/models.py:551 +#: machines/models.py:553 msgid "The range is too large, you can't create a larger one than a /16." msgstr "" "La plage est trop grande, vous ne pouvez pas en créer une plus grande " "qu'un /16." -#: machines/models.py:559 +#: machines/models.py:561 msgid "The specified range is not disjoint from existing ranges." msgstr "La plage renseignée n'est pas disjointe des plages existantes." -#: machines/models.py:573 +#: machines/models.py:575 msgid "" "If you specify a domain network or netmask, it must contain the domain's IP " "range." @@ -294,47 +298,47 @@ msgstr "" "Si vous renseignez un réseau ou masque de sous-réseau, il doit contenir la " "plage IP du domaine." -#: machines/models.py:611 +#: machines/models.py:613 msgid "v4 multicast management." msgstr "gestion de multidiffusion v4." -#: machines/models.py:612 +#: machines/models.py:614 msgid "v6 multicast management." msgstr "gestion de multidiffusion v6." -#: machines/models.py:615 +#: machines/models.py:617 msgid "Can view a VLAN object" msgstr "Peut voir un objet VLAN" -#: machines/models.py:616 machines/templates/machines/machine.html:160 +#: machines/models.py:618 machines/templates/machines/machine.html:160 msgid "VLAN" msgstr "VLAN" -#: machines/models.py:617 machines/templates/machines/sidebar.html:57 +#: machines/models.py:619 machines/templates/machines/sidebar.html:57 msgid "VLANs" msgstr "VLANs" -#: machines/models.py:629 +#: machines/models.py:631 msgid "MAC-address" msgstr "MAC-address" -#: machines/models.py:644 +#: machines/models.py:646 msgid "Can view a NAS device object" msgstr "Peut voir un objet dispositif NAS" -#: machines/models.py:645 machines/templates/machines/machine.html:164 +#: machines/models.py:647 machines/templates/machines/machine.html:164 msgid "NAS device" msgstr "dispositif NAS" -#: machines/models.py:646 machines/templates/machines/sidebar.html:63 +#: machines/models.py:648 machines/templates/machines/sidebar.html:63 msgid "NAS devices" msgstr "dispositifs NAS" -#: machines/models.py:660 +#: machines/models.py:662 msgid "Contact email address for the zone." msgstr "Adresse mail de contact pour la zone." -#: machines/models.py:664 +#: machines/models.py:666 msgid "" "Seconds before the secondary DNS have to ask the primary DNS serial to " "detect a modification." @@ -342,7 +346,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande au DNS primaire le serial pour " "détecter une modification." -#: machines/models.py:671 +#: machines/models.py:673 msgid "" "Seconds before the secondary DNS ask the serial again in case of a primary " "DNS timeout." @@ -350,7 +354,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande le serial de nouveau dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:678 +#: machines/models.py:680 msgid "" "Seconds before the secondary DNS stop answering requests in case of primary " "DNS timeout." @@ -358,121 +362,121 @@ msgstr "" "Secondes avant que le DNS secondaire arrête de répondre aux requêtes dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:683 machines/models.py:950 +#: machines/models.py:685 machines/models.py:952 msgid "Time To Live." msgstr "Temps de vie" -#: machines/models.py:687 +#: machines/models.py:689 msgid "Can view an SOA record object" msgstr "Peut voir un objet enregistrement SOA" -#: machines/models.py:688 machines/templates/machines/aff_extension.html:36 +#: machines/models.py:690 machines/templates/machines/aff_extension.html:36 #: machines/templates/machines/machine.html:120 msgid "SOA record" msgstr "enregistrement SOA" -#: machines/models.py:689 +#: machines/models.py:691 msgid "SOA records" msgstr "enregistrements SOA" -#: machines/models.py:728 +#: machines/models.py:730 msgid "SOA to edit" msgstr "SOA à modifier" -#: machines/models.py:739 +#: machines/models.py:741 msgid "Zone name, must begin with a dot (.example.org)." msgstr "Nom de zone, doit commencer par un point (.example.org)." -#: machines/models.py:747 +#: machines/models.py:749 msgid "A record associated with the zone." msgstr "Enregistrement A associé à la zone." -#: machines/models.py:753 +#: machines/models.py:755 msgid "AAAA record associated with the zone." msgstr "Enregristrement AAAA associé avec la zone." -#: machines/models.py:757 +#: machines/models.py:759 msgid "Should the zone be signed with DNSSEC." msgstr "La zone doit-elle être signée avec DNSSEC." -#: machines/models.py:762 +#: machines/models.py:764 msgid "Can view an extension object" msgstr "Peut voir un objet extension" -#: machines/models.py:763 +#: machines/models.py:765 msgid "Can use all extensions" msgstr "Peut utiliser toutes les extensions" -#: machines/models.py:765 +#: machines/models.py:767 msgid "DNS extension" msgstr "extension DNS" -#: machines/models.py:766 +#: machines/models.py:768 msgid "DNS extensions" msgstr "extensions DNS" -#: machines/models.py:826 +#: machines/models.py:828 msgid "You don't have the right to use all extensions." msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." -#: machines/models.py:835 +#: machines/models.py:837 msgid "An extension must begin with a dot." msgstr "Une extension doit commencer par un point." -#: machines/models.py:848 machines/models.py:874 machines/models.py:898 -#: machines/models.py:921 machines/models.py:1587 +#: machines/models.py:850 machines/models.py:876 machines/models.py:900 +#: machines/models.py:923 machines/models.py:1589 msgid "Time To Live (TTL)" msgstr "Temps de vie (TTL)" -#: machines/models.py:852 +#: machines/models.py:854 msgid "Can view an MX record object" msgstr "Peut voir un objet enregistrement MX" -#: machines/models.py:853 machines/templates/machines/machine.html:124 +#: machines/models.py:855 machines/templates/machines/machine.html:124 msgid "MX record" msgstr "enregistrement MX" -#: machines/models.py:854 +#: machines/models.py:856 msgid "MX records" msgstr "enregistrements MX" -#: machines/models.py:878 +#: machines/models.py:880 msgid "Can view an NS record object" msgstr "Peut voir un objet enregistrement NS" -#: machines/models.py:879 machines/templates/machines/machine.html:128 +#: machines/models.py:881 machines/templates/machines/machine.html:128 msgid "NS record" msgstr "enregistrement NS" -#: machines/models.py:880 +#: machines/models.py:882 msgid "NS records" msgstr "enregistrements NS" -#: machines/models.py:902 +#: machines/models.py:904 msgid "Can view a TXT record object" msgstr "Peut voir un objet enregistrement TXT" -#: machines/models.py:903 machines/templates/machines/machine.html:132 +#: machines/models.py:905 machines/templates/machines/machine.html:132 msgid "TXT record" msgstr "enregistrement TXT" -#: machines/models.py:904 +#: machines/models.py:906 msgid "TXT records" msgstr "enregistrements TXT" -#: machines/models.py:925 +#: machines/models.py:927 msgid "Can view a DNAME record object" msgstr "Peut voir un objet enregistrement DNAME" -#: machines/models.py:926 machines/templates/machines/machine.html:136 +#: machines/models.py:928 machines/templates/machines/machine.html:136 msgid "DNAME record" msgstr "enregistrement DNAME" -#: machines/models.py:927 +#: machines/models.py:929 msgid "DNAME records" msgstr "enregistrements DNAME" -#: machines/models.py:956 +#: machines/models.py:958 msgid "" "Priority of the target server (positive integer value, the lower it is, the " "more the server will be used if available)." @@ -480,7 +484,7 @@ msgstr "" "Priorité du serveur cible (entier positif, plus il est bas, plus le serveur " "sera utilisé si disponible)." -#: machines/models.py:965 +#: machines/models.py:967 msgid "" "Relative weight for records with the same priority (integer value between 0 " "and 65535)." @@ -488,142 +492,142 @@ msgstr "" "Poids relatif des enregistrements avec la même priorité (entier entre 0 et " "65535)." -#: machines/models.py:970 +#: machines/models.py:972 msgid "TCP/UDP port." msgstr "Port TCP/UDP." -#: machines/models.py:973 +#: machines/models.py:975 msgid "Target server." msgstr "Serveur cible." -#: machines/models.py:977 +#: machines/models.py:979 msgid "Can view an SRV record object" msgstr "Peut voir un objet enregistrement SRV" -#: machines/models.py:978 machines/templates/machines/machine.html:140 +#: machines/models.py:980 machines/templates/machines/machine.html:140 msgid "SRV record" msgstr "enregistrement SRV" -#: machines/models.py:979 +#: machines/models.py:981 msgid "SRV records" msgstr "enregistrements SRV" -#: machines/models.py:1030 +#: machines/models.py:1032 msgid "SSH public key." msgstr "Clé publique SSH." -#: machines/models.py:1033 +#: machines/models.py:1035 msgid "Comment." msgstr "Commentaire." -#: machines/models.py:1056 +#: machines/models.py:1058 msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" -#: machines/models.py:1057 machines/templates/machines/machine.html:144 +#: machines/models.py:1059 machines/templates/machines/machine.html:144 #: machines/views.py:474 msgid "SSHFP record" msgstr "enregistrement SSHFP" -#: machines/models.py:1058 +#: machines/models.py:1060 msgid "SSHFP records" msgstr "enregistrements SSHFP" -#: machines/models.py:1093 +#: machines/models.py:1095 msgid "Can view an interface object" msgstr "Peut voir un objet interface" -#: machines/models.py:1094 +#: machines/models.py:1096 msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: machines/models.py:1096 machines/views.py:358 +#: machines/models.py:1098 machines/views.py:358 msgid "interface" msgstr "interface" -#: machines/models.py:1097 +#: machines/models.py:1099 msgid "interfaces" msgstr "interfaces" -#: machines/models.py:1135 +#: machines/models.py:1137 msgid "Unknown vendor." msgstr "Constructeur inconnu." -#: machines/models.py:1199 +#: machines/models.py:1201 msgid "The given MAC address is invalid." msgstr "L'adresse MAC indiquée est invalide." -#: machines/models.py:1208 +#: machines/models.py:1210 msgid "There are no IP addresses available in the slash." msgstr "Il n'y a pas d'adresses IP disponibles dans le slash." -#: machines/models.py:1260 +#: machines/models.py:1262 msgid "The selected IP type is invalid." msgstr "Le type d'IP sélectionné est invalide." -#: machines/models.py:1274 +#: machines/models.py:1276 msgid "MAC address already registered in this machine type/subnet." msgstr "Adresse MAC déjà enregistrée dans ce type de machine/sous-réseau." -#: machines/models.py:1283 +#: machines/models.py:1285 msgid "The IPv4 address and the machine type don't match." msgstr "L'adresse IPv4 et le type de machine ne correspondent pas." -#: machines/models.py:1298 +#: machines/models.py:1300 msgid "Nonexistent machine." msgstr "Machine inexistante." -#: machines/models.py:1311 +#: machines/models.py:1313 msgid "" "You don't have the right to add an interface to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter une interface à une machine d'un autre " "utilisateur." -#: machines/models.py:1335 +#: machines/models.py:1337 msgid "You don't have the right to edit the machine." msgstr "Vous n'avez pas le droit d'éditer une machine." -#: machines/models.py:1389 machines/models.py:1509 +#: machines/models.py:1391 machines/models.py:1511 msgid "You don't have the right to view machines other than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." -#: machines/models.py:1417 +#: machines/models.py:1419 msgid "Can view an IPv6 addresses list object" msgstr "Peut voir un objet list d'adresses IPv6" -#: machines/models.py:1420 +#: machines/models.py:1422 msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: machines/models.py:1423 machines/views.py:418 +#: machines/models.py:1425 machines/views.py:418 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" -#: machines/models.py:1424 +#: machines/models.py:1426 msgid "IPv6 addresses lists" msgstr "Listes d'adresses IPv6" -#: machines/models.py:1436 machines/models.py:1677 +#: machines/models.py:1438 machines/models.py:1679 msgid "Nonexistent interface." msgstr "Interface inexistante." -#: machines/models.py:1442 machines/models.py:1686 +#: machines/models.py:1444 machines/models.py:1688 msgid "You don't have the right to add an alias to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " "utilisateur." -#: machines/models.py:1455 +#: machines/models.py:1457 msgid "You don't have the right to change the SLAAC value of an IPv6 address." msgstr "" "Vous n'avez pas le droit de changer la valeur SLAAC d'une adresse IPv6." -#: machines/models.py:1541 +#: machines/models.py:1543 msgid "A SLAAC IP address is already registered." msgstr "Une adresse IP SLAAC est déjà enregistrée." -#: machines/models.py:1555 +#: machines/models.py:1557 msgid "" "The v6 prefix is incorrect and doesn't match the type associated with the " "machine." @@ -631,49 +635,49 @@ msgstr "" "Le préfixe v6 est incorrect et ne correspond pas au type associé à la " "machine." -#: machines/models.py:1580 +#: machines/models.py:1582 msgid "Mandatory and unique, must not contain dots." msgstr "Obligatoire et unique, ne doit pas contenir de points." -#: machines/models.py:1595 +#: machines/models.py:1597 msgid "Can view a domain object" msgstr "Peut voir un objet domaine" -#: machines/models.py:1596 +#: machines/models.py:1598 msgid "Can change the TTL of a domain object" msgstr "Peut changer le TTL d'un objet domaine" -#: machines/models.py:1598 +#: machines/models.py:1600 msgid "domain" msgstr "domaine" -#: machines/models.py:1599 +#: machines/models.py:1601 msgid "domains" msgstr "domaines" -#: machines/models.py:1621 +#: machines/models.py:1623 msgid "You can't create a both A and CNAME record." msgstr "Vous ne pouvez pas créer un enregistrement à la fois A et CNAME." -#: machines/models.py:1624 +#: machines/models.py:1626 msgid "You can't create a CNAME record pointing to itself." msgstr "Vous ne pouvez pas créer un enregistrement CNAME vers lui-même." -#: machines/models.py:1630 +#: machines/models.py:1632 #, python-format msgid "The domain name %s is too long (over 63 characters)." msgstr "Le nom de domaine %s est trop long (plus de 63 caractères)." -#: machines/models.py:1634 +#: machines/models.py:1636 #, python-format msgid "The domain name %s contains forbidden characters." msgstr "Le nom de domaine %s contient des caractères interdits." -#: machines/models.py:1651 +#: machines/models.py:1653 msgid "Invalid extension." msgstr "Extension invalide." -#: machines/models.py:1702 +#: machines/models.py:1704 #, python-format msgid "" "You reached the maximum number of alias that you are allowed to create " @@ -682,164 +686,164 @@ msgstr "" "Vous avez atteint le nombre maximal d'alias que vous pouvez créer vous-même " "(%s)." -#: machines/models.py:1723 +#: machines/models.py:1725 msgid "You don't have the right to edit an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:1743 +#: machines/models.py:1745 msgid "" "You don't have the right to delete an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:1773 +#: machines/models.py:1775 msgid "You don't have the right to change the domain's TTL." msgstr "Vous n'avez pas le droit de changer le TTL du domaine." -#: machines/models.py:1790 +#: machines/models.py:1792 msgid "Can view an IPv4 addresses list object" msgstr "Peut voir un object liste d'adresses IPv4" -#: machines/models.py:1791 +#: machines/models.py:1793 msgid "IPv4 addresses list" msgstr "Liste d'adresses IPv4" -#: machines/models.py:1792 +#: machines/models.py:1794 msgid "IPv4 addresses lists" msgstr "Listes d'adresses IPv4" -#: machines/models.py:1804 +#: machines/models.py:1806 msgid "The IPv4 address and the range of the IP type don't match." msgstr "L'adresse IPv4 et la plage du type d'IP ne correspondent pas." -#: machines/models.py:1822 +#: machines/models.py:1824 msgid "DHCP server" msgstr "Serveur DHCP" -#: machines/models.py:1823 +#: machines/models.py:1825 msgid "Switches configuration server" msgstr "Serveur de configuration des commutateurs réseau" -#: machines/models.py:1824 +#: machines/models.py:1826 msgid "Recursive DNS server" msgstr "Serveur DNS récursif" -#: machines/models.py:1825 +#: machines/models.py:1827 msgid "NTP server" msgstr "Serveur NTP" -#: machines/models.py:1826 +#: machines/models.py:1828 msgid "RADIUS server" msgstr "Serveur RADIUS" -#: machines/models.py:1827 +#: machines/models.py:1829 msgid "Log server" msgstr "Serveur log" -#: machines/models.py:1828 +#: machines/models.py:1830 msgid "LDAP master server" msgstr "Serveur LDAP maître" -#: machines/models.py:1829 +#: machines/models.py:1831 msgid "LDAP backup server" msgstr "Serveur LDAP de secours" -#: machines/models.py:1830 +#: machines/models.py:1832 msgid "SMTP server" msgstr "Serveur SMTP" -#: machines/models.py:1831 +#: machines/models.py:1833 msgid "postgreSQL server" msgstr "Serveur postgreSQL" -#: machines/models.py:1832 +#: machines/models.py:1834 msgid "mySQL server" msgstr "Serveur mySQL" -#: machines/models.py:1833 +#: machines/models.py:1835 msgid "SQL client" msgstr "Client SQL" -#: machines/models.py:1834 +#: machines/models.py:1836 msgid "Gateway" msgstr "Passerelle" -#: machines/models.py:1842 +#: machines/models.py:1844 msgid "Can view a role object" msgstr "Peut voir un objet rôle" -#: machines/models.py:1843 +#: machines/models.py:1845 msgid "server role" msgstr "rôle de serveur" -#: machines/models.py:1844 +#: machines/models.py:1846 msgid "server roles" msgstr "rôles de serveur" -#: machines/models.py:1876 +#: machines/models.py:1878 msgid "Minimal time before regeneration of the service." msgstr "Temps minimal avant régénération du service." -#: machines/models.py:1880 +#: machines/models.py:1882 msgid "Maximal time before regeneration of the service." msgstr "Temps maximal avant régénération du service." -#: machines/models.py:1885 +#: machines/models.py:1887 msgid "Can view a service object" msgstr "Peut voir un objet service" -#: machines/models.py:1886 +#: machines/models.py:1888 msgid "service to generate (DHCP, DNS, ...)" msgstr "service à générer (DHCP, DNS, ...)" -#: machines/models.py:1887 +#: machines/models.py:1889 msgid "services to generate (DHCP, DNS, ...)" msgstr "services à générer (DHCP, DNS, ...)" -#: machines/models.py:1931 +#: machines/models.py:1933 msgid "Can view a service server link object" msgstr "Peut voir un objet lien service serveur" -#: machines/models.py:1933 +#: machines/models.py:1935 msgid "link between service and server" msgstr "lien entre service et serveur" -#: machines/models.py:1934 +#: machines/models.py:1936 msgid "links between service and server" msgstr "liens entre service et serveur" -#: machines/models.py:1975 +#: machines/models.py:1977 msgid "Name of the ports configuration" msgstr "Nom de la configuration de ports" -#: machines/models.py:1980 +#: machines/models.py:1982 msgid "Can view a ports opening list object" msgstr "Peut voir un objet liste d'ouverture de ports" -#: machines/models.py:1983 +#: machines/models.py:1985 msgid "ports opening list" msgstr "liste d'ouverture de ports" -#: machines/models.py:1984 +#: machines/models.py:1986 msgid "ports opening lists" msgstr "listes d'ouverture de ports" -#: machines/models.py:1995 +#: machines/models.py:1997 msgid "You don't have the right to delete a ports opening list." msgstr "Vous n'avez pas le droit de supprimer une liste d'ouverture de ports." -#: machines/models.py:1999 +#: machines/models.py:2001 msgid "This ports opening list is used." msgstr "Cette liste d'ouverture de ports est utilisée." -#: machines/models.py:2053 +#: machines/models.py:2055 msgid "ports opening" msgstr "ouverture de ports" -#: machines/models.py:2054 +#: machines/models.py:2056 msgid "ports openings" msgstr "ouvertures de ports" diff --git a/machines/models.py b/machines/models.py index 55fb8838..66866e77 100644 --- a/machines/models.py +++ b/machines/models.py @@ -542,6 +542,8 @@ class IpType(RevMixin, AclMixin, models.Model): - Qu'on ne crée pas plus gros qu'un /16 - Que le range crée ne recoupe pas un range existant - Formate l'ipv6 donnée en /64""" + if not self.domaine_ip_start or not self.domaine_ip_stop: + raise ValidationError(_("Domaine IPv4 start and stop must be valid")) if IPAddress(self.domaine_ip_start) > IPAddress(self.domaine_ip_stop): raise ValidationError(_("Range end must be after range start...")) # On ne crée pas plus grand qu'un /16 From 87e5076ca2bfe48bc0d3a1409dead951c03378ff Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 12:30:06 +0200 Subject: [PATCH 156/490] Comment SESSION_COOKIE_AGE setting --- re2o/settings_local.example.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index d0de4709..5e0e88e3 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -76,6 +76,8 @@ SESSION_COOKIE_SECURE = False CSRF_COOKIE_SECURE = False CSRF_COOKIE_HTTPONLY = False X_FRAME_OPTIONS = "DENY" + +# The validity duration of session cookies, in seconds SESSION_COOKIE_AGE = 60 * 60 * 3 # The path where your organization logo is stored From 6088f5c91b528678138f51895d86037fd66fdaf5 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 19:34:55 +0000 Subject: [PATCH 157/490] Fix CGU line formatting --- users/templates/users/user.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/user.html b/users/templates/users/user.html index 86841c8f..f6e421bb 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% if showCGU %}

{% trans "Summary of the General Terms of Use" %}

-

{{ GTU_sum_up }}

+

{{ GTU_sum_up | linebreaks }}

{% endif %}

From ec22065a349528c3df4059b55b8982dd450a7c0a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:17:06 +0200 Subject: [PATCH 158/490] Add views to get IP / MAC history --- logs/forms.py | 57 ++++++ logs/models.py | 169 ++++++++++++++++++ logs/templates/logs/machine_history.html | 59 ++++++ .../logs/search_machine_history.html | 48 +++++ logs/views.py | 25 +++ 5 files changed, 358 insertions(+) create mode 100644 logs/forms.py create mode 100644 logs/models.py create mode 100644 logs/templates/logs/machine_history.html create mode 100644 logs/templates/logs/search_machine_history.html diff --git a/logs/forms.py b/logs/forms.py new file mode 100644 index 00000000..9e2b98a6 --- /dev/null +++ b/logs/forms.py @@ -0,0 +1,57 @@ +# 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 © 2020 Jean-Romain Garnier +# +# 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. + +"""The forms used by the search app""" + +from django import forms +from django.forms import Form +from django.utils.translation import ugettext_lazy as _ +from re2o.base import get_input_formats_help_text + +CHOICES_TYPE = ( + ("ip", _("IPv4")), + ("mac", _("MAC")), +) + + +class MachineHistoryForm(Form): + """The form for a simple search""" + + q = forms.CharField( + label=_("Search"), + max_length=100, + ) + t = forms.CharField( + label=_("Search type"), + widget=forms.Select, + choices=CHOICES_TYPE, + initial=0, + ) + s = forms.DateField(required=False, label=_("Start date")) + e = forms.DateField(required=False, label=_("End date")) + + def __init__(self, *args, **kwargs): + super(MachineHistoryForm, self).__init__(*args, **kwargs) + self.fields["s"].help_text = get_input_formats_help_text( + self.fields["s"].input_formats + ) + self.fields["e"].help_text = get_input_formats_help_text( + self.fields["e"].input_formats + ) diff --git a/logs/models.py b/logs/models.py new file mode 100644 index 00000000..906fc020 --- /dev/null +++ b/logs/models.py @@ -0,0 +1,169 @@ +# -*- 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 © 2020 Jean-Romain Garnier +# +# 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. +"""machines.models +The models definitions for the Machines app +""" +from reversion.models import Version +from machines.models import IpList +from machines.models import Interface +from machines.models import Machine +from users.models import User + + +class HistoryEvent: + def __init__(self, user: User, machine: Version, interface: Version, start=None, end=None): + self.user = user + self.machine = machine + self.interface = interface + self.ipv4 = IpList.objects.get(id=interface.field_dict["ipv4_id"]).ipv4 + self.mac = self.interface.field_dict["mac_address"] + self.start_date = start + self.end_date = end + self.comment = interface.revision.get_comment() or None + + def is_similar(self, elt2): + return ( + elt2 is not None + and self.user.id == elt2.user.id + and self.ipv4 == elt2.ipv4 + and self.machine.field_dict["id"] == elt2.machine.field_dict["id"] + and self.interface.field_dict["id"] == elt2.interface.field_dict["id"] + ) + + def __repr__(self): + return "{} ({} - ): from {} to {} ({})".format( + self.machine, + self.mac, + self.ipv4, + self.start_date, + self.end_date, + self.comment or "No comment" + ) + + +class MachineHistory: + def __init__(self): + self.events = [] + self.__last_evt = None + + def get(self, search, params): + self.start = params.get("s", None) + self.end = params.get("e", None) + search_type = params.get("t", 0) + + self.events = [] + if search_type == "ip": + return self.__get_by_ip(search) + elif search_type == "mac": + return self.__get_by_mac(search) + + return None + + def __add_revision(self, user: User, machine: Version, interface: Version): + evt = HistoryEvent(user, machine, interface) + evt.start_date = interface.revision.date_created + + # Try not to recreate events if unnecessary + if evt.is_similar(self.__last_evt): + return + + # Mark the end of validity of the last element + if self.__last_evt and not self.__last_evt.end_date: + self.__last_evt.end_date = evt.start_date + + # If the event ends before the given date, remove it + if self.start and evt.start_date < self.start: + self.__last_evt = None + self.events.pop() + + # Make sure the new event starts before the given end date + if self.end and evt.start_date > self.end: + return + + # Save the new element + self.events.append(evt) + self.__last_evt = evt + + def __get_interfaces_for_ip(self, ip: str): + """ + Returns an iterable object with the Version objects + of Interfaces with the given IP + """ + # TODO: Deleted IpList + ip_id = IpList.objects.get(ipv4=ip).id + return filter( + lambda x: x.field_dict["ipv4_id"] == ip_id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + def __get_interfaces_for_mac(self, mac: str): + """ + Returns an iterable object with the Version objects + of Interfaces with the given MAC + """ + # TODO: What if IpList was deleted? + return filter( + lambda x: str(x.field_dict["mac_address"]) == mac, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + def __get_machines_for_interface(self, interface: Version): + """ + Returns an iterable object with the Verison objects + of Machines to which the given interface was attributed + """ + machine_id = interface.field_dict["machine_id"] + return filter( + lambda x: x.field_dict["id"] == machine_id, + Version.objects.get_for_model(Machine).order_by("revision__date_created") + ) + + def __get_user_for_machine(self, machine: Version): + """ + Returns the user to which the given machine belongs + """ + # TODO: What if user was deleted? + user_id = machine.field_dict["user_id"] + return User.objects.get(id=user_id) + + def __get_by_ip(self, ip: str): + interfaces = self.__get_interfaces_for_ip(ip) + + for interface in interfaces: + machines = self.__get_machines_for_interface(interface) + + for machine in machines: + user = self.__get_user_for_machine(machine) + self.__add_revision(user, machine, interface) + + return self.events + + def __get_by_mac(self, mac: str): + interfaces = self.__get_interfaces_for_mac(mac) + + for interface in interfaces: + machines = self.__get_machines_for_interface(interface) + + for machine in machines: + user = self.__get_user_for_machine(machine) + self.__add_revision(user, machine, interface) + + return self.events diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html new file mode 100644 index 00000000..01d6c7e7 --- /dev/null +++ b/logs/templates/logs/machine_history.html @@ -0,0 +1,59 @@ +{% extends 'log/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 © 2020 Jean-Romain Garnier + +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 "Search results" %}{% endblock %} + +{% block content %} + {% if events %} + + + + + + + + + + + + {% for event in events %} + + + + + + + + + {% endfor %} +
UserIPv4MACStart dateEnd dateComment
{{ event.user.pseudo }}{{ event.ipv4 }}{{ event.mac }}{{ event.start_date }}{{ event.end_date }}{{ event.comment }}
+ {% else %} +

{% trans "No result" %}

+ {% endif %} +
+
+
+{% endblock %} diff --git a/logs/templates/logs/search_machine_history.html b/logs/templates/logs/search_machine_history.html new file mode 100644 index 00000000..3baf563d --- /dev/null +++ b/logs/templates/logs/search_machine_history.html @@ -0,0 +1,48 @@ +{% extends 'logs/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 © 2020 Jean-Romain Garnier + +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 "Search" %}{% endblock %} + +{% block content %} + +
+ {% bootstrap_field history_form.q %} + {% bootstrap_form_errors history_form.t %} + {% if history_form.s %} + {% bootstrap_field history_form.s %} + {% endif %} + {% if history_form.e %} + {% bootstrap_field history_form.e %} + {% endif %} + {% trans "Search" as tr_search %} + {% bootstrap_button tr_search button_type="submit" icon="search" %} +
+
+
+
+
+
+{% endblock %} diff --git a/logs/views.py b/logs/views.py index 78971d18..08ac0c9f 100644 --- a/logs/views.py +++ b/logs/views.py @@ -101,6 +101,9 @@ from re2o.utils import ( from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history +from .models import MachineHistory +from .forms import MachineHistoryForm + @login_required @can_view_app("logs") @@ -478,6 +481,28 @@ def stats_actions(request): return render(request, "logs/stats_users.html", {"stats_list": stats}) +@login_required +@can_view_app("users") +def search_machine_history(request): + """Vue qui permet de rechercher l'historique des machines ayant utilisé + une IP ou une adresse MAC""" + history_form = MachineHistoryForm(request.GET or None) + if history_form.is_valid(): + history = MachineHistory() + return render( + request, + "logs/machine_history.html", + { + "events": + history.get( + history_form.cleaned_data.get("q", ""), + history_form.cleaned_data + ) + }, + ) + return render(request, "logs/search_machine_history.html", {"history_form": history_form}) + + def history(request, application, object_name, object_id): """Render history for a model. From 7bce33a84f44fdac5702f7cdfbd8eeecd35e2511 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 16:32:56 +0000 Subject: [PATCH 159/490] Fix machine history views --- logs/forms.py | 4 +- logs/templates/logs/machine_history.html | 2 +- .../logs/search_machine_history.html | 10 ++-- logs/urls.py | 1 + logs/views.py | 2 +- test.py | 50 +++++++++++++++++++ 6 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 test.py diff --git a/logs/forms.py b/logs/forms.py index 9e2b98a6..f51dbef7 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -40,9 +40,7 @@ class MachineHistoryForm(Form): ) t = forms.CharField( label=_("Search type"), - widget=forms.Select, - choices=CHOICES_TYPE, - initial=0, + widget=forms.Select(choices=CHOICES_TYPE) ) s = forms.DateField(required=False, label=_("Start date")) e = forms.DateField(required=False, label=_("End date")) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index 01d6c7e7..50efbcf5 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -1,4 +1,4 @@ -{% extends 'log/sidebar.html' %} +{% extends 'logs/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 diff --git a/logs/templates/logs/search_machine_history.html b/logs/templates/logs/search_machine_history.html index 3baf563d..07c9f68c 100644 --- a/logs/templates/logs/search_machine_history.html +++ b/logs/templates/logs/search_machine_history.html @@ -24,19 +24,17 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} -{% block title %}{% trans "Search" %}{% endblock %} +{% block title %}{% trans "Search machine history" %}{% endblock %} {% block content %}
+

{% trans "Search machine history" %}

+ {% bootstrap_field history_form.q %} - {% bootstrap_form_errors history_form.t %} - {% if history_form.s %} + {% bootstrap_field history_form.t %} {% bootstrap_field history_form.s %} - {% endif %} - {% if history_form.e %} {% bootstrap_field history_form.e %} - {% endif %} {% trans "Search" as tr_search %} {% bootstrap_button tr_search button_type="submit" icon="search" %}
diff --git a/logs/urls.py b/logs/urls.py index 8fa0f469..eced2a83 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -46,4 +46,5 @@ urlpatterns = [ views.history, name="history", ), + url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), ] diff --git a/logs/views.py b/logs/views.py index 08ac0c9f..ca75e48f 100644 --- a/logs/views.py +++ b/logs/views.py @@ -483,7 +483,7 @@ def stats_actions(request): @login_required @can_view_app("users") -def search_machine_history(request): +def stats_search_machine_history(request): """Vue qui permet de rechercher l'historique des machines ayant utilisé une IP ou une adresse MAC""" history_form = MachineHistoryForm(request.GET or None) diff --git a/test.py b/test.py new file mode 100644 index 00000000..7b777609 --- /dev/null +++ b/test.py @@ -0,0 +1,50 @@ +from reversion.models import Version +from machines.models import IpList +from machines.models import Interface +from machines.models import Machine +from users.models import User + + +def get_interfaces_with_ip(ip): + """ + Get all the interfaces that, at some point, had the given ip + """ + # TODO: What if IpList was deleted? + ip_id = IpList.objects.get(ipv4=ip).id + interfaces = Version.objects.get_for_model(Interface) + interfaces = filter(lambda x: x.field_dict["ipv4_id"] == ip_id, interfaces) + return interfaces + + +def get_machine_with_interface(interface): + """ + Get the machine which contained the given interface, even if deleted + """ + machine_id = interface.field_dict["machine_id"] + machines = Version.objects.get_for_model(Machine) + machines = filter(lambda x: x.field_dict["id"] == machine_id, machines) + return machines[0] + + +def get_user_with_machine(machine): + """ + """ + user_id = machine.field_dict["user_id"] + user = User.objects.filter(id=user_id) + return user[0] + + +interfaces = get_interfaces_with_ip("10.0.0.0") + +output_dict = {} +for interface in interfaces: + mac = interface.field_dict["mac_address"] + machine = get_machine_with_interface(interface) + user = get_user_with_machine(machine) + output_dict[mac] = { + "machine": machine.field_dict["name"], + "user": user + } + +print(output_dict) + From 448d9dbd92ae4e167f2158b53501f2506449f781 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:39:05 +0200 Subject: [PATCH 160/490] Add comments to logs/models.py --- logs/models.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/logs/models.py b/logs/models.py index 906fc020..59466405 100644 --- a/logs/models.py +++ b/logs/models.py @@ -40,6 +40,9 @@ class HistoryEvent: self.comment = interface.revision.get_comment() or None def is_similar(self, elt2): + """ + Checks whether two events are similar enough to be merged + """ return ( elt2 is not None and self.user.id == elt2.user.id @@ -78,10 +81,13 @@ class MachineHistory: return None def __add_revision(self, user: User, machine: Version, interface: Version): + """ + Add a new revision to the chronological order + """ evt = HistoryEvent(user, machine, interface) evt.start_date = interface.revision.date_created - # Try not to recreate events if unnecessary + # Try not to recreate events if it's unnecessary if evt.is_similar(self.__last_evt): return @@ -107,8 +113,12 @@ class MachineHistory: Returns an iterable object with the Version objects of Interfaces with the given IP """ - # TODO: Deleted IpList - ip_id = IpList.objects.get(ipv4=ip).id + # TODO: What if ip list was deleted? + try: + ip_id = IpList.objects.get(ipv4=ip).id + except IpList.DoesNotExist: + return [] + return filter( lambda x: x.field_dict["ipv4_id"] == ip_id, Version.objects.get_for_model(Interface).order_by("revision__date_created") @@ -119,7 +129,6 @@ class MachineHistory: Returns an iterable object with the Version objects of Interfaces with the given MAC """ - # TODO: What if IpList was deleted? return filter( lambda x: str(x.field_dict["mac_address"]) == mac, Version.objects.get_for_model(Interface).order_by("revision__date_created") From b085c006bbe8603395d7435284243779a0eba96c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:41:59 +0200 Subject: [PATCH 161/490] Improve machine history results view --- logs/templates/logs/machine_history.html | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index 50efbcf5..4af9bf16 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -31,17 +31,21 @@ with this program; if not, write to the Free Software Foundation, Inc., - - - - - - + + + + + + {% for event in events %} - + From 8d25dd3731bca034e87ccb9d7638257cac5116f1 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:43:13 +0200 Subject: [PATCH 162/490] Tweak machine history results view --- logs/templates/logs/machine_history.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index 4af9bf16..c2ad7368 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., - + @@ -43,7 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., From a03047a6ae731f7c8003da92613ef19f7c0befd5 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:45:43 +0200 Subject: [PATCH 163/490] Handle empty dates in machine history results view --- logs/templates/logs/machine_history.html | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index c2ad7368..e1c510ed 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -48,8 +48,20 @@ with this program; if not, write to the Free Software Foundation, Inc., - - + + {% endfor %} From 8aacef28f30c723e99a31e1e7b71e99f8c5d1351 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:47:21 +0200 Subject: [PATCH 164/490] Add link to machine history view --- logs/templates/logs/sidebar.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/logs/templates/logs/sidebar.html b/logs/templates/logs/sidebar.html index 7f7d6cbf..a44da9e3 100644 --- a/logs/templates/logs/sidebar.html +++ b/logs/templates/logs/sidebar.html @@ -52,6 +52,10 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Users" %} + + + {% trans "Machine history" %} + {% acl_end %} {% endblock %} From fa439f9e9ceff377ffa1dcef898d9e580d0a7d64 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:51:11 +0200 Subject: [PATCH 165/490] Fix date compare in machine history view --- logs/models.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/logs/models.py b/logs/models.py index 59466405..0c70c739 100644 --- a/logs/models.py +++ b/logs/models.py @@ -22,6 +22,8 @@ The models definitions for the Machines app """ from reversion.models import Version +from datetime import datetime + from machines.models import IpList from machines.models import Interface from machines.models import Machine @@ -72,6 +74,21 @@ class MachineHistory: self.end = params.get("e", None) search_type = params.get("t", 0) + # Convert dates to datetime objects + if self.start: + self.start = datetime.datetime( + self.start.year, + self.start.month, + self.start.day + ) + + if self.end: + self.end = datetime.datetime( + self.end.year, + self.end.month, + self.end.day + ) + self.events = [] if search_type == "ip": return self.__get_by_ip(search) From bc688d39f9b82696c413df6f987f7c272642cb4a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 18:52:58 +0200 Subject: [PATCH 166/490] Handle dates more gracefuly in machine history view --- logs/models.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/logs/models.py b/logs/models.py index 0c70c739..f54b8365 100644 --- a/logs/models.py +++ b/logs/models.py @@ -22,7 +22,6 @@ The models definitions for the Machines app """ from reversion.models import Version -from datetime import datetime from machines.models import IpList from machines.models import Interface @@ -74,21 +73,6 @@ class MachineHistory: self.end = params.get("e", None) search_type = params.get("t", 0) - # Convert dates to datetime objects - if self.start: - self.start = datetime.datetime( - self.start.year, - self.start.month, - self.start.day - ) - - if self.end: - self.end = datetime.datetime( - self.end.year, - self.end.month, - self.end.day - ) - self.events = [] if search_type == "ip": return self.__get_by_ip(search) @@ -113,12 +97,12 @@ class MachineHistory: self.__last_evt.end_date = evt.start_date # If the event ends before the given date, remove it - if self.start and evt.start_date < self.start: + if self.start and evt.start_date.date() < self.start: self.__last_evt = None self.events.pop() # Make sure the new event starts before the given end date - if self.end and evt.start_date > self.end: + if self.end and evt.start_date.date() > self.end: return # Save the new element From a9f587ba180c21c5da18201e4886034df7a52c82 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 16:55:59 +0000 Subject: [PATCH 167/490] Fix date check in machine history display --- logs/templates/logs/machine_history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index e1c510ed..93e1817a 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% endfor %}
UserIPv4MACStart dateEnd dateComment{% trans "User" %}{% trans "User" %}{% trans "MAC address" %}{% trans "Start date" %}{% trans "End date" %}{% trans "Comment" %}
{{ event.user.pseudo }} + + {{ event.user }} + + {{ event.ipv4 }} {{ event.mac }} {{ event.start_date }}
{% trans "User" %}{% trans "User" %}{% trans "IPv4" %} {% trans "MAC address" %} {% trans "Start date" %} {% trans "End date" %}
- {{ event.user }} + {{ event.user }} {{ event.ipv4 }}{{ event.ipv4 }} {{ event.mac }}{{ event.start_date }}{{ event.end_date }} + {% if event.start_date %} + {{ event.start_date }} + {% else %} + {% trans "Unknown" %} + {% endif %} + + {% if event.start_date %} + {{ event.end_date }} + {% else %} + {% trans "Now" %} + {% endif %} + {{ event.comment }}
- {% if event.start_date %} + {% if event.end_date %} {{ event.end_date }} {% else %} {% trans "Now" %} From c891e521185ba5be9f451a80a24a9132be4508a0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 17:02:33 +0000 Subject: [PATCH 168/490] Add missing translations in logs --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/forms.py | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 126 ++++++++++++++------ machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 5 +- 13 files changed, 103 insertions(+), 50 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 8040325f..3c08e4e5 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index ef9b3fa5..aca2607b 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/forms.py b/logs/forms.py index f51dbef7..3182ef17 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -27,7 +27,7 @@ from re2o.base import get_input_formats_help_text CHOICES_TYPE = ( ("ip", _("IPv4")), - ("mac", _("MAC")), + ("mac", _("MAC address")), ) diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 88f515fd..bb8dc944 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,6 +34,30 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." +#: logs/forms.py:29 logs/templates/logs/machine_history.html:35 +msgid "IPv4" +msgstr "IPv4" + +#: logs/forms.py:30 logs/templates/logs/machine_history.html:36 +msgid "MAC address" +msgstr "Adresse MAC" + +#: logs/forms.py:38 logs/templates/logs/search_machine_history.html:38 +msgid "Search" +msgstr "Rechercher" + +#: logs/forms.py:42 +msgid "Search type" +msgstr "Type de recherche" + +#: logs/forms.py:45 logs/templates/logs/machine_history.html:37 +msgid "Start date" +msgstr "Date de début" + +#: logs/forms.py:46 logs/templates/logs/machine_history.html:38 +msgid "End date" +msgstr "Date de fin" + #: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" @@ -52,6 +76,7 @@ msgid "Date of editing" msgstr "Date de modification" #: logs/templates/logs/aff_stats_logs.html:42 +#: logs/templates/logs/machine_history.html:39 msgid "Comment" msgstr "Commentaire" @@ -159,10 +184,35 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:418 +#: logs/views.py:421 msgid "Actions performed" msgstr "Actions effectuées" +#: logs/templates/logs/machine_history.html:27 +msgid "Search results" +msgstr "Résultats de la recherche" + +#: logs/templates/logs/machine_history.html:34 +msgid "User" +msgstr "Utilisateur" + +#: logs/templates/logs/machine_history.html:55 +msgid "Unknown" +msgstr "Inconnu(e)" + +#: logs/templates/logs/machine_history.html:62 +msgid "Now" +msgstr "Maintenant" + +#: logs/templates/logs/machine_history.html:70 +msgid "No result" +msgstr "Aucun résultat" + +#: logs/templates/logs/search_machine_history.html:27 +#: logs/templates/logs/search_machine_history.html:32 +msgid "Search machine history" +msgstr "Rechercher l'historique des machines" + #: logs/templates/logs/sidebar.html:33 msgid "Summary" msgstr "Résumé" @@ -187,6 +237,10 @@ msgstr "Actions de câblage" msgid "Users" msgstr "Utilisateurs" +#: logs/templates/logs/sidebar.html:57 +msgid "Machine history" +msgstr "Historique des machines" + #: logs/templates/logs/stats_general.html:32 msgid "General statistics" msgstr "Statistiques générales" @@ -199,138 +253,138 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: logs/views.py:175 +#: logs/views.py:178 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: logs/views.py:178 +#: logs/views.py:181 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: logs/views.py:219 +#: logs/views.py:222 msgid "Category" msgstr "Catégorie" -#: logs/views.py:220 +#: logs/views.py:223 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: logs/views.py:221 +#: logs/views.py:224 msgid "Number of members" msgstr "Nombre d'adhérents" -#: logs/views.py:222 +#: logs/views.py:225 msgid "Number of clubs" msgstr "Nombre de clubs" -#: logs/views.py:226 +#: logs/views.py:229 msgid "Activated users" msgstr "Utilisateurs activés" -#: logs/views.py:232 +#: logs/views.py:235 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: logs/views.py:238 +#: logs/views.py:241 msgid "Archived users" msgstr "Utilisateurs archivés" -#: logs/views.py:244 +#: logs/views.py:247 msgid "Fully archived users" msgstr "Utilisateurs complètement archivés" -#: logs/views.py:254 +#: logs/views.py:257 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:264 +#: logs/views.py:267 msgid "Contributing members" msgstr "Adhérents cotisants" -#: logs/views.py:270 +#: logs/views.py:273 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: logs/views.py:276 +#: logs/views.py:279 msgid "Banned users" msgstr "Utilisateurs bannis" -#: logs/views.py:282 +#: logs/views.py:285 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: logs/views.py:288 +#: logs/views.py:291 msgid "Users with a confirmed email" msgstr "Utilisateurs ayant un mail confirmé" -#: logs/views.py:294 +#: logs/views.py:297 msgid "Users with an unconfirmed email" msgstr "Utilisateurs ayant un mail non confirmé" -#: logs/views.py:300 +#: logs/views.py:303 msgid "Users pending email confirmation" msgstr "Utilisateurs en attente de confirmation du mail" -#: logs/views.py:306 +#: logs/views.py:309 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:320 +#: logs/views.py:323 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:337 +#: logs/views.py:340 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:338 +#: logs/views.py:341 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:339 +#: logs/views.py:342 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:340 +#: logs/views.py:343 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:341 +#: logs/views.py:344 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:342 +#: logs/views.py:345 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:357 +#: logs/views.py:360 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:403 +#: logs/views.py:406 msgid "Topology" msgstr "Topologie" -#: logs/views.py:419 +#: logs/views.py:422 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:444 +#: logs/views.py:447 msgid "rights" msgstr "droits" -#: logs/views.py:473 +#: logs/views.py:476 msgid "actions" msgstr "actions" -#: logs/views.py:504 +#: logs/views.py:529 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:510 +#: logs/views.py:535 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:517 +#: logs/views.py:542 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index e5f8dfc9..f1726d25 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 22:29+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index e60cb464..14c22e92 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index e90c1e2a..c655e7a3 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 84b4bd34..92498917 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 2e18a0a2..0c644ed6 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index ad8e9968..89d40143 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index df651ec7..fbb7cbc2 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index b5f45bff..a637304c 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index edae2c02..6ce34b36 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-21 21:38+0200\n" +"POT-Creation-Date: 2020-04-22 19:00+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -492,8 +492,7 @@ msgstr "Le champ mail ne peut pas ^êêtre vide" #: users/models.py:1344 msgid "You can't use a {} address as an external contact address." -msgstr "" -"Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." +msgstr "Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." #: users/models.py:1371 msgid "member" From 3e52ac48d6ec1f09c3fed70d7cb1818e5020a409 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 19:10:10 +0200 Subject: [PATCH 169/490] Add pagination for machine history results --- logs/templates/logs/machine_history.html | 1 + logs/views.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index 93e1817a..5aea5e9d 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -66,6 +66,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
+ {% include 'pagination.html' with list=events %} {% else %}

{% trans "No result" %}

{% endif %} diff --git a/logs/views.py b/logs/views.py index ca75e48f..7e6df54a 100644 --- a/logs/views.py +++ b/logs/views.py @@ -489,16 +489,19 @@ def stats_search_machine_history(request): history_form = MachineHistoryForm(request.GET or None) if history_form.is_valid(): history = MachineHistory() + events = history.get( + history_form.cleaned_data.get("q", ""), + history_form.cleaned_data + ) + max_result = GeneralOption.get_cached_value("search_display_page") + re2o_paginator(request, + events, + max_result) + return render( request, "logs/machine_history.html", - { - "events": - history.get( - history_form.cleaned_data.get("q", ""), - history_form.cleaned_data - ) - }, + { "events": events }, ) return render(request, "logs/search_machine_history.html", {"history_form": history_form}) From d5e0494663bf7e137d1984b972abc4ad87101594 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 17:14:49 +0000 Subject: [PATCH 170/490] Fix number of results in pagination for machine history --- logs/views.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/logs/views.py b/logs/views.py index 7e6df54a..2ee1b0c7 100644 --- a/logs/views.py +++ b/logs/views.py @@ -493,10 +493,12 @@ def stats_search_machine_history(request): history_form.cleaned_data.get("q", ""), history_form.cleaned_data ) - max_result = GeneralOption.get_cached_value("search_display_page") - re2o_paginator(request, - events, - max_result) + max_result = GeneralOption.get_cached_value("pagination_number") + events = re2o_paginator( + request, + events, + max_result + ) return render( request, From 636b291d790c17712a8bff3a5691444cf748ed3c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 19:48:30 +0200 Subject: [PATCH 171/490] Remove unused file --- test.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 test.py diff --git a/test.py b/test.py deleted file mode 100644 index 7b777609..00000000 --- a/test.py +++ /dev/null @@ -1,50 +0,0 @@ -from reversion.models import Version -from machines.models import IpList -from machines.models import Interface -from machines.models import Machine -from users.models import User - - -def get_interfaces_with_ip(ip): - """ - Get all the interfaces that, at some point, had the given ip - """ - # TODO: What if IpList was deleted? - ip_id = IpList.objects.get(ipv4=ip).id - interfaces = Version.objects.get_for_model(Interface) - interfaces = filter(lambda x: x.field_dict["ipv4_id"] == ip_id, interfaces) - return interfaces - - -def get_machine_with_interface(interface): - """ - Get the machine which contained the given interface, even if deleted - """ - machine_id = interface.field_dict["machine_id"] - machines = Version.objects.get_for_model(Machine) - machines = filter(lambda x: x.field_dict["id"] == machine_id, machines) - return machines[0] - - -def get_user_with_machine(machine): - """ - """ - user_id = machine.field_dict["user_id"] - user = User.objects.filter(id=user_id) - return user[0] - - -interfaces = get_interfaces_with_ip("10.0.0.0") - -output_dict = {} -for interface in interfaces: - mac = interface.field_dict["mac_address"] - machine = get_machine_with_interface(interface) - user = get_user_with_machine(machine) - output_dict[mac] = { - "machine": machine.field_dict["name"], - "user": user - } - -print(output_dict) - From 6d8fa424873d81ca43c92927c5c8e1e5ae1cfeee Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Wed, 22 Apr 2020 23:23:12 +0200 Subject: [PATCH 172/490] Fix comments for machine history view and model --- logs/models.py | 62 ++++++++++++++++++++++++++++++++++++-------------- logs/views.py | 4 ++-- 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/logs/models.py b/logs/models.py index f54b8365..343a33cb 100644 --- a/logs/models.py +++ b/logs/models.py @@ -18,8 +18,8 @@ # 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. -"""machines.models -The models definitions for the Machines app +"""logs.models +The models definitions for the logs app """ from reversion.models import Version @@ -30,7 +30,14 @@ from users.models import User class HistoryEvent: - def __init__(self, user: User, machine: Version, interface: Version, start=None, end=None): + def __init__(self, user, machine, interface, start=None, end=None): + """ + :param user: User, The user owning the maching at the time of the event + :param machine: Version, the machine version related to the interface + :param interface: Version, the interface targeted by this event + :param start: datetime, the date at which this version was created + :param end: datetime, the date at which this version was replace by a new one + """ self.user = user self.machine = machine self.interface = interface @@ -43,6 +50,7 @@ class HistoryEvent: def is_similar(self, elt2): """ Checks whether two events are similar enough to be merged + :return: bool """ return ( elt2 is not None @@ -69,6 +77,11 @@ class MachineHistory: self.__last_evt = None def get(self, search, params): + """ + :param search: ip or mac to lookup + :param params: dict built by the search view + :return: list or None, a list of HistoryEvent + """ self.start = params.get("s", None) self.end = params.get("e", None) search_type = params.get("t", 0) @@ -81,9 +94,12 @@ class MachineHistory: return None - def __add_revision(self, user: User, machine: Version, interface: Version): + def __add_revision(self, user, machine, interface): """ Add a new revision to the chronological order + :param user: User, The user owning the maching at the time of the event + :param machine: Version, the machine version related to the interface + :param interface: Version, the interface targeted by this event """ evt = HistoryEvent(user, machine, interface) evt.start_date = interface.revision.date_created @@ -109,10 +125,11 @@ class MachineHistory: self.events.append(evt) self.__last_evt = evt - def __get_interfaces_for_ip(self, ip: str): + def __get_interfaces_for_ip(self, ip): """ - Returns an iterable object with the Version objects - of Interfaces with the given IP + :param ip: str + :return: An iterable object with the Version objects + of Interfaces with the given IP """ # TODO: What if ip list was deleted? try: @@ -125,20 +142,22 @@ class MachineHistory: Version.objects.get_for_model(Interface).order_by("revision__date_created") ) - def __get_interfaces_for_mac(self, mac: str): + def __get_interfaces_for_mac(self, mac): """ - Returns an iterable object with the Version objects - of Interfaces with the given MAC + :param mac: str + :return: An iterable object with the Version objects + of Interfaces with the given MAC address """ return filter( lambda x: str(x.field_dict["mac_address"]) == mac, Version.objects.get_for_model(Interface).order_by("revision__date_created") ) - def __get_machines_for_interface(self, interface: Version): + def __get_machines_for_interface(self, interface): """ - Returns an iterable object with the Verison objects - of Machines to which the given interface was attributed + :param interface: Version, the interface for which to find the machines + :return: An iterable object with the Version objects of Machine to + which the given interface was attributed """ machine_id = interface.field_dict["machine_id"] return filter( @@ -146,15 +165,20 @@ class MachineHistory: Version.objects.get_for_model(Machine).order_by("revision__date_created") ) - def __get_user_for_machine(self, machine: Version): + def __get_user_for_machine(self, machine): """ - Returns the user to which the given machine belongs + :param machine: Version, the machine of which the owner must be found + :return: The user to which the given machine belongs """ # TODO: What if user was deleted? user_id = machine.field_dict["user_id"] return User.objects.get(id=user_id) - def __get_by_ip(self, ip: str): + def __get_by_ip(self, ip): + """ + :param ip: str, The IP to lookup + :returns: list, a list of HistoryEvent + """ interfaces = self.__get_interfaces_for_ip(ip) for interface in interfaces: @@ -166,7 +190,11 @@ class MachineHistory: return self.events - def __get_by_mac(self, mac: str): + def __get_by_mac(self, mac): + """ + :param mac: str, The MAC address to lookup + :returns: list, a list of HistoryEvent + """ interfaces = self.__get_interfaces_for_mac(mac) for interface in interfaces: diff --git a/logs/views.py b/logs/views.py index 2ee1b0c7..85ba35cf 100644 --- a/logs/views.py +++ b/logs/views.py @@ -484,8 +484,8 @@ def stats_actions(request): @login_required @can_view_app("users") def stats_search_machine_history(request): - """Vue qui permet de rechercher l'historique des machines ayant utilisé - une IP ou une adresse MAC""" + """View which displays the history of machines with the given + une IP or MAC adresse""" history_form = MachineHistoryForm(request.GET or None) if history_form.is_valid(): history = MachineHistory() From 502d9606ca54a3ab72a2ea31be05370a92aaa4a5 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 22 Apr 2020 18:12:37 +0200 Subject: [PATCH 173/490] Create preferences models and edition template --- preferences/locale/fr/LC_MESSAGES/django.po | 346 ++++++++++---------- preferences/models.py | 26 +- preferences/urls.py | 19 +- preferences/utils/__init__.py | 0 preferences/utils/models.py | 55 ++++ preferences/utils/views.py | 68 ++++ preferences/views.py | 28 +- 7 files changed, 310 insertions(+), 232 deletions(-) create mode 100644 preferences/utils/__init__.py create mode 100644 preferences/utils/models.py create mode 100644 preferences/utils/views.py diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index c655e7a3..d034ac9b 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -256,57 +256,57 @@ msgstr "Modèles de document actuels" msgid "Current attributes" msgstr "Attributs actuels" -#: preferences/models.py:78 +#: preferences/models.py:56 msgid "Users can't select their room" msgstr "Les utilisateurs ne peuvent pas modifier leur chambre" -#: preferences/models.py:79 +#: preferences/models.py:57 msgid "" "Users can only select a room occupied by a user with a disabled connection." msgstr "" "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la " "connexion est désactivée." -#: preferences/models.py:80 +#: preferences/models.py:58 msgid "Users can select all rooms" msgstr "Les utilisateurs peuvent choisir toutes les chambres" -#: preferences/models.py:86 +#: preferences/models.py:64 msgid "Users can create a club." msgstr "Les utilisateurs peuvent créer un club." -#: preferences/models.py:89 +#: preferences/models.py:67 msgid "Users can create a member." msgstr "Les utilisateurs peuvent créer un adhérent." -#: preferences/models.py:95 +#: preferences/models.py:73 msgid "Users can edit their shell." msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande." -#: preferences/models.py:101 +#: preferences/models.py:79 msgid "Policy on self users room edition" msgstr "Autorisation d'édtion du champ chambre par les utilisateurs" -#: preferences/models.py:104 +#: preferences/models.py:82 msgid "Enable local email accounts for users." msgstr "Activer les comptes mail locaux pour les utilisateurs." -#: preferences/models.py:109 +#: preferences/models.py:87 msgid "Domain to use for local email accounts." msgstr "Domaine à utiliser pour les comptes mail locaux." -#: preferences/models.py:113 +#: preferences/models.py:91 msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:118 +#: preferences/models.py:96 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:124 +#: preferences/models.py:102 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -314,11 +314,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:128 +#: preferences/models.py:106 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:133 +#: preferences/models.py:111 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -326,7 +326,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:140 +#: preferences/models.py:118 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -337,172 +337,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:147 +#: preferences/models.py:125 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:151 +#: preferences/models.py:129 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:152 +#: preferences/models.py:130 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:159 +#: preferences/models.py:137 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:177 +#: preferences/models.py:155 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:178 +#: preferences/models.py:156 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:179 +#: preferences/models.py:157 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:188 +#: preferences/models.py:166 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:198 +#: preferences/models.py:176 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:199 +#: preferences/models.py:177 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:219 preferences/models.py:677 +#: preferences/models.py:197 preferences/models.py:655 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:220 preferences/models.py:678 +#: preferences/models.py:198 preferences/models.py:656 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:226 +#: preferences/models.py:204 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:231 +#: preferences/models.py:209 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:237 +#: preferences/models.py:215 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:244 +#: preferences/models.py:222 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:250 +#: preferences/models.py:228 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:253 +#: preferences/models.py:231 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:256 +#: preferences/models.py:234 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:360 +#: preferences/models.py:338 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:361 +#: preferences/models.py:339 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:374 +#: preferences/models.py:352 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:376 +#: preferences/models.py:354 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:379 +#: preferences/models.py:357 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:383 +#: preferences/models.py:361 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:384 preferences/views.py:331 +#: preferences/models.py:362 preferences/views.py:307 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:385 +#: preferences/models.py:363 #: preferences/templates/preferences/display_preferences.html:221 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:392 +#: preferences/models.py:370 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:395 +#: preferences/models.py:373 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:401 +#: preferences/models.py:379 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:402 +#: preferences/models.py:380 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:404 +#: preferences/models.py:382 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:411 +#: preferences/models.py:389 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:414 preferences/views.py:394 +#: preferences/models.py:392 preferences/views.py:370 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:417 +#: preferences/models.py:395 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:429 +#: preferences/models.py:407 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:435 +#: preferences/models.py:413 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:439 +#: preferences/models.py:417 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:440 preferences/views.py:276 +#: preferences/models.py:418 preferences/views.py:252 msgid "reminder" msgstr "rappel" -#: preferences/models.py:441 +#: preferences/models.py:419 msgid "reminders" msgstr "rappels" -#: preferences/models.py:462 +#: preferences/models.py:440 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -510,7 +510,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:470 +#: preferences/models.py:448 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -518,75 +518,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:485 +#: preferences/models.py:463 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:486 +#: preferences/models.py:464 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:506 +#: preferences/models.py:484 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:507 preferences/views.py:227 +#: preferences/models.py:485 preferences/views.py:203 msgid "service" msgstr "service" -#: preferences/models.py:508 +#: preferences/models.py:486 msgid "services" msgstr "services" -#: preferences/models.py:518 +#: preferences/models.py:496 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:524 +#: preferences/models.py:502 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:534 +#: preferences/models.py:512 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:536 +#: preferences/models.py:514 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:537 +#: preferences/models.py:515 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:545 preferences/views.py:634 +#: preferences/models.py:523 preferences/views.py:610 msgid "mandate" msgstr "mandat" -#: preferences/models.py:546 +#: preferences/models.py:524 msgid "mandates" msgstr "mandats" -#: preferences/models.py:547 +#: preferences/models.py:525 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:554 +#: preferences/models.py:532 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:555 +#: preferences/models.py:533 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:557 +#: preferences/models.py:535 msgid "start date" msgstr "date de début" -#: preferences/models.py:558 +#: preferences/models.py:536 msgid "end date" msgstr "date de fin" -#: preferences/models.py:571 +#: preferences/models.py:549 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -594,140 +594,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:586 +#: preferences/models.py:564 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:589 +#: preferences/models.py:567 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:590 +#: preferences/models.py:568 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:593 +#: preferences/models.py:571 msgid "Organisation" msgstr "Association" -#: preferences/models.py:600 +#: preferences/models.py:578 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:601 +#: preferences/models.py:579 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:619 +#: preferences/models.py:597 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:620 +#: preferences/models.py:598 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:634 +#: preferences/models.py:612 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:637 +#: preferences/models.py:615 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:642 +#: preferences/models.py:620 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:644 +#: preferences/models.py:622 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:649 +#: preferences/models.py:627 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:650 +#: preferences/models.py:628 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:654 preferences/views.py:587 +#: preferences/models.py:632 preferences/views.py:563 msgid "attribute" msgstr "attribut" -#: preferences/models.py:655 +#: preferences/models.py:633 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:657 +#: preferences/models.py:635 msgid "value" msgstr "valeur" -#: preferences/models.py:659 +#: preferences/models.py:637 msgid "comment" msgstr "commentaire" -#: preferences/models.py:660 +#: preferences/models.py:638 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:671 +#: preferences/models.py:649 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:672 +#: preferences/models.py:650 #: preferences/templates/preferences/display_preferences.html:299 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:683 +#: preferences/models.py:661 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:684 +#: preferences/models.py:662 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:693 +#: preferences/models.py:671 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:701 +#: preferences/models.py:679 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:702 +#: preferences/models.py:680 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:708 +#: preferences/models.py:686 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:709 +#: preferences/models.py:687 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:715 +#: preferences/models.py:693 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:723 +#: preferences/models.py:701 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:724 +#: preferences/models.py:702 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:730 +#: preferences/models.py:708 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:731 +#: preferences/models.py:709 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:738 +#: preferences/models.py:716 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -735,87 +735,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:748 +#: preferences/models.py:726 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:749 +#: preferences/models.py:727 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:755 +#: preferences/models.py:733 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:756 +#: preferences/models.py:734 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:762 +#: preferences/models.py:740 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:770 +#: preferences/models.py:748 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:771 +#: preferences/models.py:749 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:777 +#: preferences/models.py:755 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:778 +#: preferences/models.py:756 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:784 +#: preferences/models.py:762 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:792 +#: preferences/models.py:770 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:793 +#: preferences/models.py:771 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:799 +#: preferences/models.py:777 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:800 +#: preferences/models.py:778 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:813 +#: preferences/models.py:791 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:814 +#: preferences/models.py:792 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:841 +#: preferences/models.py:819 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:845 +#: preferences/models.py:823 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:852 +#: preferences/models.py:830 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:858 +#: preferences/models.py:836 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:860 +#: preferences/models.py:838 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -823,19 +823,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:872 +#: preferences/models.py:850 msgid "template" msgstr "modèle" -#: preferences/models.py:873 +#: preferences/models.py:851 msgid "name" msgstr "nom" -#: preferences/models.py:876 +#: preferences/models.py:854 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:877 +#: preferences/models.py:855 msgid "document templates" msgstr "modèles de document" @@ -1035,9 +1035,9 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:417 #: preferences/templates/preferences/display_preferences.html:495 #: preferences/templates/preferences/edit_preferences.html:46 -#: preferences/views.py:212 preferences/views.py:261 preferences/views.py:307 -#: preferences/views.py:367 preferences/views.py:431 preferences/views.py:496 -#: preferences/views.py:572 preferences/views.py:619 +#: preferences/views.py:188 preferences/views.py:237 preferences/views.py:283 +#: preferences/views.py:343 preferences/views.py:407 preferences/views.py:472 +#: preferences/views.py:548 preferences/views.py:595 msgid "Edit" msgstr "Modifier" @@ -1359,75 +1359,75 @@ msgstr "URL du compte Facebook" msgid "Editing of preferences" msgstr "Modification des préférences" -#: preferences/views.py:160 +#: preferences/utils/views.py:45 msgid "Unknown object." msgstr "Objet inconnu." -#: preferences/views.py:179 +#: preferences/utils/views.py:64 msgid "The preferences were edited." msgstr "Les préférences ont été modifiées." -#: preferences/views.py:191 +#: preferences/views.py:167 msgid "The service was added." msgstr "Le service a été ajouté." -#: preferences/views.py:194 preferences/views.py:243 preferences/views.py:292 -#: preferences/views.py:349 preferences/views.py:412 preferences/views.py:471 -#: preferences/views.py:554 preferences/views.py:603 +#: preferences/views.py:170 preferences/views.py:219 preferences/views.py:268 +#: preferences/views.py:325 preferences/views.py:388 preferences/views.py:447 +#: preferences/views.py:530 preferences/views.py:579 msgid "Add" msgstr "Ajouter" -#: preferences/views.py:209 +#: preferences/views.py:185 msgid "The service was edited." msgstr "Le service a été modifié." -#: preferences/views.py:224 +#: preferences/views.py:200 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: preferences/views.py:240 +#: preferences/views.py:216 msgid "The reminder was added." msgstr "Le rappel a été ajouté." -#: preferences/views.py:258 +#: preferences/views.py:234 msgid "The reminder was edited." msgstr "Le rappel a été modifié." -#: preferences/views.py:273 +#: preferences/views.py:249 msgid "The reminder was deleted." msgstr "Le rappel a été supprimé." -#: preferences/views.py:289 +#: preferences/views.py:265 msgid "The RADIUS key was added." msgstr "La clé RADIUS a été ajoutée." -#: preferences/views.py:304 +#: preferences/views.py:280 msgid "The RADIUS key was edited." msgstr "La clé RADIUS a été modifiée." -#: preferences/views.py:320 +#: preferences/views.py:296 msgid "The RADIUS key was deleted." msgstr "La clé RADIUS a été supprimée." -#: preferences/views.py:325 +#: preferences/views.py:301 msgid "The RADIUS key is assigned to at least one switch, you can't delete it." msgstr "" "La clé RADIUS est assignée a au moins un commutateur réseau, vous ne pouvez " "pas la supprimer." -#: preferences/views.py:344 +#: preferences/views.py:320 msgid "The switch management credentials were added." msgstr "Les identifiants de gestion de commutateur réseay ont été ajoutés." -#: preferences/views.py:364 +#: preferences/views.py:340 msgid "The switch management credentials were edited." msgstr "Les identifiants de gestion de commutateur réseau ont été modifiés." -#: preferences/views.py:381 +#: preferences/views.py:357 msgid "The switch management credentials were deleted." msgstr "Les identifiants de gestion de commutateur réseau ont été supprimés." -#: preferences/views.py:387 +#: preferences/views.py:363 msgid "" "The switch management credentials are assigned to at least one switch, you " "can't delete them." @@ -1435,44 +1435,44 @@ msgstr "" "Les identifiants de gestion de commutateur réseau sont assignés à au moins " "un commutateur réseau , vous ne pouvez pas les supprimer." -#: preferences/views.py:407 +#: preferences/views.py:383 msgid "The contact email address was created." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:428 +#: preferences/views.py:404 msgid "The contact email address was edited." msgstr "L'adresse mail de contact a été modifiée." -#: preferences/views.py:446 +#: preferences/views.py:422 msgid "The contact email adress was deleted." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:449 preferences/views.py:536 +#: preferences/views.py:425 preferences/views.py:512 msgid "Delete" msgstr "Supprimer" -#: preferences/views.py:466 +#: preferences/views.py:442 msgid "The document template was created." msgstr "Le modèle de document a été créé." -#: preferences/views.py:472 +#: preferences/views.py:448 msgid "New document template" msgstr "Nouveau modèle de document" -#: preferences/views.py:491 +#: preferences/views.py:467 msgid "The document template was edited." msgstr "Le modèle de document a été édité." -#: preferences/views.py:497 +#: preferences/views.py:473 msgid "Edit document template" msgstr "Modifier le modèle de document" -#: preferences/views.py:520 +#: preferences/views.py:496 #, python-format msgid "The document template %(document_template)s was deleted." msgstr "Le modèle de document %(document_template)s a été supprimé." -#: preferences/views.py:527 +#: preferences/views.py:503 #, python-format msgid "" "The document template %(document_template)s can't be deleted because it is " @@ -1481,31 +1481,31 @@ msgstr "" "Le modèle de document %(document_template)s ne peut pas être supprimé car il " "est actuellement utilisé." -#: preferences/views.py:537 +#: preferences/views.py:513 msgid "Delete document template" msgstr "Supprimer le modèle de document" -#: preferences/views.py:551 +#: preferences/views.py:527 msgid "The attribute was added." msgstr "L'attribut a été ajouté." -#: preferences/views.py:569 +#: preferences/views.py:545 msgid "The attribute was edited." msgstr "L'attribut a été modifié." -#: preferences/views.py:584 +#: preferences/views.py:560 msgid "The attribute was deleted." msgstr "L'attribut a été supprimé." -#: preferences/views.py:600 +#: preferences/views.py:576 msgid "The mandate was added." msgstr "Le mandat a été ajouté." -#: preferences/views.py:616 +#: preferences/views.py:592 msgid "The mandate was edited." msgstr "Le mandat a été modifié." -#: preferences/views.py:631 +#: preferences/views.py:607 msgid "The mandate was deleted." msgstr "Le mandat été supprimé." diff --git a/preferences/models.py b/preferences/models.py index 66a72f46..b52a5f07 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -37,36 +37,14 @@ from django.utils.translation import ugettext_lazy as _ import machines.models +from .utils.models import PreferencesModel + from re2o.mixins import AclMixin, RevMixin from re2o.aes_field import AESEncryptedField from datetime import timedelta -class PreferencesModel(models.Model): - """ Base object for the Preferences objects - Defines methods to handle the cache of the settings (they should - not change a lot) """ - - @classmethod - def set_in_cache(cls): - """ Save the preferences in a server-side cache """ - instance, _created = cls.objects.get_or_create() - cache.set(cls().__class__.__name__.lower(), instance, None) - return instance - - @classmethod - def get_cached_value(cls, key): - """ Get the preferences from the server-side cache """ - instance = cache.get(cls().__class__.__name__.lower()) - if instance is None: - instance = cls.set_in_cache() - return getattr(instance, key) - - class Meta: - abstract = True - - class OptionalUser(AclMixin, PreferencesModel): """Options pour l'user : obligation ou nom du telephone, activation ou non du solde, autorisation du negatif, fingerprint etc""" diff --git a/preferences/urls.py b/preferences/urls.py index 4e5b1bcf..82780cb8 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -28,52 +28,53 @@ from __future__ import unicode_literals from django.conf.urls import url from . import views +from .views import edit_options urlpatterns = [ url( r"^edit_options/(?P
OptionalUser)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
OptionalMachine)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
OptionalTopologie)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
GeneralOption)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
AssoOption)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
HomeOption)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
MailMessageOption)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
RadiusOption)$", - views.edit_options, + edit_options, name="edit-options", ), url( r"^edit_options/(?P
CotisationsOption)$", - views.edit_options, + edit_options, name="edit-options", ), url(r"^add_service/$", views.add_service, name="add-service"), diff --git a/preferences/utils/__init__.py b/preferences/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/preferences/utils/models.py b/preferences/utils/models.py new file mode 100644 index 00000000..5ade54e1 --- /dev/null +++ b/preferences/utils/models.py @@ -0,0 +1,55 @@ +# 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 © 2020 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. + +# App de gestion des machines pour re2o +# Gabriel Détraz, Augustin Lemesle +# Gplv2 +""" +Utils for preferences +""" + +from __future__ import unicode_literals + +from django.core.cache import cache +from django.db import models + + +class PreferencesModel(models.Model): + """ Base object for the Preferences objects + Defines methods to handle the cache of the settings (they should + not change a lot) """ + + @classmethod + def set_in_cache(cls): + """ Save the preferences in a server-side cache """ + instance, _created = cls.objects.get_or_create() + cache.set(cls().__class__.__name__.lower(), instance, None) + return instance + + @classmethod + def get_cached_value(cls, key): + """ Get the preferences from the server-side cache """ + instance = cache.get(cls().__class__.__name__.lower()) + if instance is None: + instance = cls.set_in_cache() + return getattr(instance, key) + + class Meta: + abstract = True diff --git a/preferences/utils/views.py b/preferences/utils/views.py new file mode 100644 index 00000000..978b9b98 --- /dev/null +++ b/preferences/utils/views.py @@ -0,0 +1,68 @@ +# 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 © 2020 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. + +# App de gestion des machines pour re2o +# Gabriel Détraz, Augustin Lemesle +# Gplv2 +""" +Utils for preferences +""" + +from __future__ import unicode_literals +from django.urls import reverse +from django.shortcuts import redirect +from django.contrib import messages +from django.db.models import ProtectedError +from django.db import transaction +from django.utils.translation import ugettext as _ + +from reversion import revisions as reversion + +from re2o.views import form + +def edit_options_template_function(request, section, forms, models): + """ Edition des préférences générales""" + model = getattr(models, section, None) + form_instance = getattr(forms, "Edit" + section + "Form", None) + if not (model or form_instance): + messages.error(request, _("Unknown object.")) + return redirect(reverse("preferences:display-options")) + + options_instance, _created = model.objects.get_or_create() + can, msg, permissions = options_instance.can_edit(request.user) + if not can: + messages.error(request, acl_error_message(msg, permissions)) + return redirect(reverse("index")) + options = form_instance( + request.POST or None, request.FILES or None, instance=options_instance + ) + if options.is_valid(): + with transaction.atomic(), reversion.create_revision(): + options.save() + reversion.set_user(request.user) + reversion.set_comment( + "Field(s) edited: %s" + % ", ".join(field for field in options.changed_data) + ) + messages.success(request, _("The preferences were edited.")) + return redirect(reverse("preferences:display-options")) + return form({"options": options}, "preferences/edit_preferences.html", request) + + diff --git a/preferences/views.py b/preferences/views.py index a7df9cef..06d9871e 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -86,6 +86,7 @@ from .models import ( from . import models from . import forms +from .utils.views import edit_options_template_function @login_required @can_view_all( @@ -153,32 +154,7 @@ def display_options(request): @login_required def edit_options(request, section): - """ Edition des préférences générales""" - model = getattr(models, section, None) - form_instance = getattr(forms, "Edit" + section + "Form", None) - if not (model or form_instance): - messages.error(request, _("Unknown object.")) - return redirect(reverse("preferences:display-options")) - - options_instance, _created = model.objects.get_or_create() - can, msg, permissions = options_instance.can_edit(request.user) - if not can: - messages.error(request, acl_error_message(msg, permissions)) - return redirect(reverse("index")) - options = form_instance( - request.POST or None, request.FILES or None, instance=options_instance - ) - if options.is_valid(): - with transaction.atomic(), reversion.create_revision(): - options.save() - reversion.set_user(request.user) - reversion.set_comment( - "Field(s) edited: %s" - % ", ".join(field for field in options.changed_data) - ) - messages.success(request, _("The preferences were edited.")) - return redirect(reverse("preferences:display-options")) - return form({"options": options}, "preferences/edit_preferences.html", request) + return edit_options_template_function(request, section, forms, models) @login_required From 648eb223ee05431cb45242292c36eabf287b9726 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 22 Apr 2020 18:55:33 +0200 Subject: [PATCH 174/490] New organisation for local apps and all settings in folder preferences --- tickets/admin.py | 36 ++++++++++- tickets/forms.py | 26 ++++++++ tickets/locale/fr/LC_MESSAGES/django.po | 63 +++++++++++-------- tickets/migrations/0003_auto_20200422_1839.py | 23 +++++++ tickets/models.py | 36 +++++++++-- tickets/preferences/forms.py | 38 ++++++++++- tickets/preferences/models.py | 33 +++++++++- tickets/preferences/views.py | 60 ++++++++++++++++++ tickets/templates/tickets/preferences.html | 2 +- tickets/urls.py | 34 ++++++++-- tickets/views.py | 46 +------------- 11 files changed, 311 insertions(+), 86 deletions(-) create mode 100644 tickets/migrations/0003_auto_20200422_1839.py create mode 100644 tickets/preferences/views.py diff --git a/tickets/admin.py b/tickets/admin.py index 6a20b775..69f84d0d 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -1,5 +1,37 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Ticket preferences model +""" + + from django.contrib import admin from .models import Ticket -admin.site.register(Ticket) -# Register your models here. +from reversion.admin import VersionAdmin + +class TicketAdmin(VersionAdmin): + """Gestion des ticket""" + + pass + +admin.site.register(Ticket, TicketAdmin) diff --git a/tickets/forms.py b/tickets/forms.py index 00edb0ec..4f62db84 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -1,3 +1,29 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Ticket form +""" + + from django import forms from django.forms import ModelForm, Form from re2o.field_permissions import FieldPermissionFormMixin diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index fbb7cbc2..528bf611 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -30,66 +30,78 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/models.py:28 +#: tickets/models.py:54 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:32 +#: tickets/models.py:58 msgid "Description of the ticket." msgstr "Description du ticket." -#: tickets/models.py:38 +#: tickets/models.py:64 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:44 +#: tickets/models.py:70 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:45 +#: tickets/models.py:71 msgid "ticket" msgstr "ticket" -#: tickets/models.py:46 +#: tickets/models.py:72 msgid "tickets" msgstr "tickets" -#: tickets/models.py:50 +#: tickets/models.py:76 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:52 +#: tickets/models.py:78 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:85 +#: tickets/models.py:111 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:97 +#: tickets/models.py:123 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." -#: tickets/preferences/models.py:10 +#: tickets/preferences/forms.py:44 +msgid "Publish address" +msgstr "Adresse mail de publication" + +#: tickets/preferences/forms.py:45 +msgid "Mail language" +msgstr "Langue du mail" + +#: tickets/preferences/models.py:38 msgid "" "Email address to publish the new tickets (leave empty for no publication)." msgstr "" "Adresse mail où publier les nouveaux tickets (laissez vide pour ne pas " "publier)." -#: tickets/preferences/models.py:17 +#: tickets/preferences/models.py:45 msgid "French" msgstr "Français" -#: tickets/preferences/models.py:17 +#: tickets/preferences/models.py:45 msgid "English" msgstr "Anglais" -#: tickets/preferences/models.py:21 -msgid "tickets preferences" -msgstr "préférences de tickets" +#: tickets/preferences/models.py:49 +msgid "tickets options" +msgstr "Options des tickets" + +#: tickets/preferences/models.py:50 +msgid "Can view tickets options" +msgstr "Peut voir les options des tickets" #: tickets/templates/tickets/aff_ticket.html:30 #: tickets/templates/tickets/contact.html:4 @@ -214,7 +226,7 @@ msgstr "Modifier" msgid "Ticket opening" msgstr "Ouverture de ticket" -#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:90 +#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:85 msgid "" "You are not authenticated. Please log in or provide an email address so we " "can get back to you." @@ -280,21 +292,22 @@ msgstr "Langue du mail" msgid "No tickets" msgstr "Pas de tickets" -#: tickets/views.py:71 tickets/views.py:82 +#: tickets/views.py:66 tickets/views.py:77 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:127 tickets/views.py:177 +#: tickets/views.py:122 tickets/views.py:147 msgid "Never" msgstr "Jamais" -#: tickets/views.py:154 -msgid "The tickets preferences were edited." -msgstr "Les préférences de tickets ont été modifiées." +#~ msgid "The tickets preferences were edited." +#~ msgstr "Les préférences de tickets ont été modifiées." -#: tickets/views.py:157 -msgid "Invalid form." -msgstr "Formulaire invalide." +#~ msgid "Invalid form." +#~ msgstr "Formulaire invalide." + +#~ msgid "tickets preferences" +#~ msgstr "préférences de tickets" diff --git a/tickets/migrations/0003_auto_20200422_1839.py b/tickets/migrations/0003_auto_20200422_1839.py new file mode 100644 index 00000000..daa29e42 --- /dev/null +++ b/tickets/migrations/0003_auto_20200422_1839.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-22 16:39 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0002_auto_20191120_0159'), + ] + + operations = [ + migrations.RenameModel( + old_name='Preferences', + new_name='TicketOption', + ), + migrations.AlterModelOptions( + name='ticketoption', + options={'permissions': (('view_ticketoption', 'Can view tickets options'),), 'verbose_name': 'tickets options'}, + ), + ] diff --git a/tickets/models.py b/tickets/models.py index a8adbe87..bf0a3794 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -1,3 +1,29 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Ticket model +""" + + from django.db import models from django.utils.translation import ugettext_lazy as _ from django.template import loader @@ -11,7 +37,7 @@ from preferences.models import GeneralOption import users.models -from .preferences.models import Preferences +from .preferences.models import TicketOption class Ticket(AclMixin, models.Model): @@ -52,11 +78,11 @@ class Ticket(AclMixin, models.Model): return _("Anonymous ticket. Date: %s.") % (self.date) def publish_mail(self, request=None): - site_url = GeneralOption.objects.first().main_site_url - to_addr = Preferences.objects.first().publish_address + site_url = GeneralOption.get_cached_value("main_site_url") + to_addr = TicketOption.get_cached_value("publish_address") context = {"ticket": self, "site_url": site_url} - lang = Preferences.objects.first().mail_language + lang = TicketOption.get_cached_value("mail_language") if lang == 0: obj = "Nouveau ticket ouvert" template = loader.get_template("tickets/publication_mail_fr") @@ -109,6 +135,6 @@ class Ticket(AclMixin, models.Model): def ticket_post_save(**kwargs): """ Send the mail to publish the new ticket """ if kwargs["created"]: - if Preferences.objects.first().publish_address: + if TicketOption.get_cached_value("publish_address"): ticket = kwargs["instance"] ticket.publish_mail(ticket.request) diff --git a/tickets/preferences/forms.py b/tickets/preferences/forms.py index b12bde67..05b40ec3 100644 --- a/tickets/preferences/forms.py +++ b/tickets/preferences/forms.py @@ -1,13 +1,45 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Ticket preferences form +""" + from django import forms from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ -from .models import Preferences +from re2o.mixins import FormRevMixin +from .models import TicketOption -class EditPreferencesForm(ModelForm): +class EditTicketOptionForm(FormRevMixin, ModelForm): """ Edit the ticket's settings""" class Meta: - model = Preferences + model = TicketOption fields = "__all__" + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(EditTicketOptionForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields["publish_address"].label = _("Publish address") + self.fields["mail_language"].label = _("Mail language") diff --git a/tickets/preferences/models.py b/tickets/preferences/models.py index 27922303..8b41c5eb 100644 --- a/tickets/preferences/models.py +++ b/tickets/preferences/models.py @@ -1,8 +1,36 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Ticket preferences model +""" + + from django.db import models from django.utils.translation import ugettext_lazy as _ +from re2o.mixins import AclMixin, RevMixin +from preferences.utils.models import PreferencesModel -class Preferences(models.Model): +class TicketOption(AclMixin, PreferencesModel): """ Definition of the ticket's settings""" publish_address = models.EmailField( @@ -18,4 +46,5 @@ class Preferences(models.Model): mail_language = models.IntegerField(choices=LANGUES, default=LANG_FR) class Meta: - verbose_name = _("tickets preferences") + verbose_name = _("tickets options") + permissions = (("view_ticketoption", _("Can view tickets options")),) diff --git a/tickets/preferences/views.py b/tickets/preferences/views.py new file mode 100644 index 00000000..cfc6a0f2 --- /dev/null +++ b/tickets/preferences/views.py @@ -0,0 +1,60 @@ +# -*- 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 © 2020 Gabriel Détraz +# Copyright © 2019 Arthur Grisel-Davy +# +# 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 + +from django.contrib import messages +from django.contrib.auth.decorators import login_required +from django.shortcuts import render, redirect +from django.template.loader import render_to_string +from django.views.decorators.cache import cache_page +from django.utils.translation import ugettext as _ +from django.urls import reverse +from django.forms import modelformset_factory +from re2o.views import form + +from re2o.base import re2o_paginator + +from re2o.acl import can_view, can_view_all, can_edit, can_create + +from preferences.utils.views import edit_options_template_function + +from . import forms +from . import models + +def aff_preferences(request): + """ View to display the settings of the tickets in the preferences page""" + pref, created = models.TicketOption.objects.get_or_create() + context = { + "preferences": pref, + "language": str(pref.LANGUES[pref.mail_language][1]), + } + return render_to_string( + "tickets/preferences.html", context=context, request=request, using=None + ) + +@login_required +def edit_options(request, section): + return edit_options_template_function(request, section, forms, models) + diff --git a/tickets/templates/tickets/preferences.html b/tickets/templates/tickets/preferences.html index df51f3e1..5952a496 100644 --- a/tickets/templates/tickets/preferences.html +++ b/tickets/templates/tickets/preferences.html @@ -9,7 +9,7 @@
- + {% trans "Edit" %} diff --git a/tickets/urls.py b/tickets/urls.py index 1dfda475..c6eaefde 100644 --- a/tickets/urls.py +++ b/tickets/urls.py @@ -1,14 +1,40 @@ +# -*- 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 © 2019 Arthur Grisel-Davy +# Copyright © 2020 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. +""" +Tickets url +""" + from django.conf.urls import url from . import views +from .preferences.views import edit_options urlpatterns = [ url(r"^$", views.aff_tickets, name="aff-tickets"), - url(r"^ticket/(?P[0-9]+)$", views.aff_ticket, name="aff-ticket"), + url(r"^(?P[0-9]+)$", views.aff_ticket, name="aff-ticket"), url( - r"^ticket/edit-preferences-tickets$", - views.edit_preferences, - name="edit-preferences-tickets", + r"^edit_options/(?P
TicketOption)$", + edit_options, + name="edit-options", ), url(r"^new_ticket/$", views.new_ticket, name="new-ticket"), ] diff --git a/tickets/views.py b/tickets/views.py index 03cc535c..f5186cdf 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -3,9 +3,8 @@ # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # -# Copyright © 2017 Gabriel Détraz -# Copyright © 2017 Lara Kermarec -# Copyright © 2017 Augustin Lemesle +# Copyright © 2019 Arthur Grisel-Davy +# Copyright © 2020 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 @@ -43,12 +42,8 @@ from preferences.models import GeneralOption from .models import Ticket -from .preferences.models import Preferences - from .forms import NewTicketForm, ChangeStatusTicketForm -from .preferences.forms import EditPreferencesForm - def new_ticket(request): """ Ticket creation view""" @@ -140,31 +135,6 @@ def aff_tickets(request): return render(request, "tickets/index.html", context=context) -def edit_preferences(request): - """ View to edit the settings of the tickets """ - - preferences_instance, created = Preferences.objects.get_or_create(id=1) - preferencesform = EditPreferencesForm( - request.POST or None, instance=preferences_instance - ) - - if preferencesform.is_valid(): - if preferencesform.changed_data: - preferencesform.save() - messages.success(request, _("The tickets preferences were edited.")) - return redirect(reverse("preferences:display-options")) - else: - messages.error(request, _("Invalid form.")) - return form( - {"preferencesform": preferencesform}, - "tickets/form_preferences.html", - request, - ) - return form( - {"preferencesform": preferencesform}, "tickets/form_preferences.html", request - ) - - # views cannoniques des apps optionnels def profil(request, user): """ View to display the ticket's module on the profil""" @@ -191,18 +161,6 @@ def profil(request, user): ) -def preferences(request): - """ View to display the settings of the tickets in the preferences page""" - pref, created = Preferences.objects.get_or_create(id=1) - context = { - "preferences": pref, - "language": str(pref.LANGUES[pref.mail_language][1]), - } - return render_to_string( - "tickets/preferences.html", context=context, request=request, using=None - ) - - def contact(request): """View to display a contact address on the contact page used here to display a link to open a ticket""" From 6ca39663d64ed18748c60c505d4f9bfffc0c03f1 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 22 Apr 2020 18:59:04 +0200 Subject: [PATCH 175/490] New place for local preferences --- preferences/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/preferences/views.py b/preferences/views.py index 06d9871e..1bc31b2b 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -121,9 +121,9 @@ def display_options(request): optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O] optionnal_templates_list = [ - app.views.preferences(request) + app.preferences.views.aff_preferences(request) for app in optionnal_apps - if hasattr(app.views, "preferences") + if hasattr(app.preferences.views, "aff_preferences") ] return form( From 721c0a1a195db01d797e74caadc527f27f13deb1 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 22 Apr 2020 19:35:45 +0200 Subject: [PATCH 176/490] No need for preferences/utils --- preferences/models.py | 51 ++++++++++++++++++++------ preferences/utils/__init__.py | 0 preferences/utils/models.py | 55 ---------------------------- preferences/utils/views.py | 68 ----------------------------------- preferences/views.py | 45 +++++++++++++++++------ tickets/preferences/models.py | 3 +- tickets/preferences/views.py | 5 +-- 7 files changed, 80 insertions(+), 147 deletions(-) delete mode 100644 preferences/utils/__init__.py delete mode 100644 preferences/utils/models.py delete mode 100644 preferences/utils/views.py diff --git a/preferences/models.py b/preferences/models.py index b52a5f07..58badc18 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -37,14 +37,36 @@ from django.utils.translation import ugettext_lazy as _ import machines.models -from .utils.models import PreferencesModel - from re2o.mixins import AclMixin, RevMixin from re2o.aes_field import AESEncryptedField from datetime import timedelta +class PreferencesModel(models.Model): + """ Base object for the Preferences objects + Defines methods to handle the cache of the settings (they should + not change a lot) """ + + @classmethod + def set_in_cache(cls): + """ Save the preferences in a server-side cache """ + instance, _created = cls.objects.get_or_create() + cache.set(cls().__class__.__name__.lower(), instance, None) + return instance + + @classmethod + def get_cached_value(cls, key): + """ Get the preferences from the server-side cache """ + instance = cache.get(cls().__class__.__name__.lower()) + if instance is None: + instance = cls.set_in_cache() + return getattr(instance, key) + + class Meta: + abstract = True + + class OptionalUser(AclMixin, PreferencesModel): """Options pour l'user : obligation ou nom du telephone, activation ou non du solde, autorisation du negatif, fingerprint etc""" @@ -54,7 +76,12 @@ class OptionalUser(AclMixin, PreferencesModel): ALL_ROOM = "ALL_ROOM" ROOM_POLICY = ( (DISABLED, _("Users can't select their room")), - (ONLY_INACTIVE, _("Users can only select a room occupied by a user with a disabled connection.")), + ( + ONLY_INACTIVE, + _( + "Users can only select a room occupied by a user with a disabled connection." + ), + ), (ALL_ROOM, _("Users can select all rooms")), ) @@ -76,7 +103,7 @@ class OptionalUser(AclMixin, PreferencesModel): max_length=32, choices=ROOM_POLICY, default="DISABLED", - help_text=_("Policy on self users room edition") + help_text=_("Policy on self users room edition"), ) local_email_accounts_enabled = models.BooleanField( default=False, help_text=_("Enable local email accounts for users.") @@ -92,9 +119,7 @@ class OptionalUser(AclMixin, PreferencesModel): ) delete_notyetactive = models.IntegerField( default=15, - help_text=_( - "Not yet active users will be deleted after this number of days." - ), + help_text=_("Not yet active users will be deleted after this number of days."), ) disable_emailnotyetconfirmed = models.IntegerField( default=2, @@ -195,7 +220,7 @@ class OptionalTopologie(AclMixin, PreferencesModel): DEFINED = "DEFINED" CHOICE_RADIUS = ( (MACHINE, _("On the IP range's VLAN of the machine")), - (DEFINED, _("Preset in \"VLAN for machines accepted by RADIUS\"")), + (DEFINED, _('Preset in "VLAN for machines accepted by RADIUS"')), ) CHOICE_PROVISION = (("sftp", "SFTP"), ("tftp", "TFTP")) @@ -335,7 +360,9 @@ class OptionalTopologie(AclMixin, PreferencesModel): ) class Meta: - permissions = (("view_optionaltopologie", _("Can view the topology preferences")),) + permissions = ( + ("view_optionaltopologie", _("Can view the topology preferences")), + ) verbose_name = _("topology preferences") @@ -546,7 +573,9 @@ class Mandate(RevMixin, AclMixin, models.Model): ) if not mandate: raise cls.DoesNotExist( - _("No mandates have been created. Please go to the preferences page to create one.") + _( + "No mandates have been created. Please go to the preferences page to create one." + ) ) return mandate @@ -653,7 +682,7 @@ class RadiusOption(AclMixin, PreferencesModel): DEFINED = "DEFINED" CHOICE_RADIUS = ( (MACHINE, _("On the IP range's VLAN of the machine")), - (DEFINED, _("Preset in \"VLAN for machines accepted by RADIUS\"")), + (DEFINED, _('Preset in "VLAN for machines accepted by RADIUS"')), ) REJECT = "REJECT" SET_VLAN = "SET_VLAN" diff --git a/preferences/utils/__init__.py b/preferences/utils/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/preferences/utils/models.py b/preferences/utils/models.py deleted file mode 100644 index 5ade54e1..00000000 --- a/preferences/utils/models.py +++ /dev/null @@ -1,55 +0,0 @@ -# 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 © 2020 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. - -# App de gestion des machines pour re2o -# Gabriel Détraz, Augustin Lemesle -# Gplv2 -""" -Utils for preferences -""" - -from __future__ import unicode_literals - -from django.core.cache import cache -from django.db import models - - -class PreferencesModel(models.Model): - """ Base object for the Preferences objects - Defines methods to handle the cache of the settings (they should - not change a lot) """ - - @classmethod - def set_in_cache(cls): - """ Save the preferences in a server-side cache """ - instance, _created = cls.objects.get_or_create() - cache.set(cls().__class__.__name__.lower(), instance, None) - return instance - - @classmethod - def get_cached_value(cls, key): - """ Get the preferences from the server-side cache """ - instance = cache.get(cls().__class__.__name__.lower()) - if instance is None: - instance = cls.set_in_cache() - return getattr(instance, key) - - class Meta: - abstract = True diff --git a/preferences/utils/views.py b/preferences/utils/views.py deleted file mode 100644 index 978b9b98..00000000 --- a/preferences/utils/views.py +++ /dev/null @@ -1,68 +0,0 @@ -# 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 © 2020 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. - -# App de gestion des machines pour re2o -# Gabriel Détraz, Augustin Lemesle -# Gplv2 -""" -Utils for preferences -""" - -from __future__ import unicode_literals -from django.urls import reverse -from django.shortcuts import redirect -from django.contrib import messages -from django.db.models import ProtectedError -from django.db import transaction -from django.utils.translation import ugettext as _ - -from reversion import revisions as reversion - -from re2o.views import form - -def edit_options_template_function(request, section, forms, models): - """ Edition des préférences générales""" - model = getattr(models, section, None) - form_instance = getattr(forms, "Edit" + section + "Form", None) - if not (model or form_instance): - messages.error(request, _("Unknown object.")) - return redirect(reverse("preferences:display-options")) - - options_instance, _created = model.objects.get_or_create() - can, msg, permissions = options_instance.can_edit(request.user) - if not can: - messages.error(request, acl_error_message(msg, permissions)) - return redirect(reverse("index")) - options = form_instance( - request.POST or None, request.FILES or None, instance=options_instance - ) - if options.is_valid(): - with transaction.atomic(), reversion.create_revision(): - options.save() - reversion.set_user(request.user) - reversion.set_comment( - "Field(s) edited: %s" - % ", ".join(field for field in options.changed_data) - ) - messages.success(request, _("The preferences were edited.")) - return redirect(reverse("preferences:display-options")) - return form({"options": options}, "preferences/edit_preferences.html", request) - - diff --git a/preferences/views.py b/preferences/views.py index 1bc31b2b..6420dad3 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -86,7 +86,35 @@ from .models import ( from . import models from . import forms -from .utils.views import edit_options_template_function + +def edit_options_template_function(request, section, forms, models): + """ Edition des préférences générales""" + model = getattr(models, section, None) + form_instance = getattr(forms, "Edit" + section + "Form", None) + if not (model or form_instance): + messages.error(request, _("Unknown object.")) + return redirect(reverse("preferences:display-options")) + + options_instance, _created = model.objects.get_or_create() + can, msg, permissions = options_instance.can_edit(request.user) + if not can: + messages.error(request, acl_error_message(msg, permissions)) + return redirect(reverse("index")) + options = form_instance( + request.POST or None, request.FILES or None, instance=options_instance + ) + if options.is_valid(): + with transaction.atomic(), reversion.create_revision(): + options.save() + reversion.set_user(request.user) + reversion.set_comment( + "Field(s) edited: %s" + % ", ".join(field for field in options.changed_data) + ) + messages.success(request, _("The preferences were edited.")) + return redirect(reverse("preferences:display-options")) + return form({"options": options}, "preferences/edit_preferences.html", request) + @login_required @can_view_all( @@ -320,10 +348,7 @@ def add_switchmanagementcred(request): messages.success(request, _("The switch management credentials were added.")) return redirect(reverse("preferences:display-options")) return form( - { - "preferenceform": switchmanagementcred, - "action_name": _("Add"), - }, + {"preferenceform": switchmanagementcred, "action_name": _("Add"),}, "preferences/preferences.html", request, ) @@ -367,7 +392,10 @@ def del_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs): ) return redirect(reverse("preferences:display-options")) return form( - {"objet": switchmanagementcred_instance, "objet_name": _("switch management credentials")}, + { + "objet": switchmanagementcred_instance, + "objet_name": _("switch management credentials"), + }, "preferences/delete.html", request, ) @@ -383,10 +411,7 @@ def add_mailcontact(request): messages.success(request, _("The contact email address was created.")) return redirect(reverse("preferences:display-options")) return form( - { - "preferenceform": mailcontact, - "action_name": _("Add"), - }, + {"preferenceform": mailcontact, "action_name": _("Add"),}, "preferences/preferences.html", request, ) diff --git a/tickets/preferences/models.py b/tickets/preferences/models.py index 8b41c5eb..abc4d5fd 100644 --- a/tickets/preferences/models.py +++ b/tickets/preferences/models.py @@ -28,7 +28,8 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from re2o.mixins import AclMixin, RevMixin -from preferences.utils.models import PreferencesModel +from preferences.models import PreferencesModel + class TicketOption(AclMixin, PreferencesModel): """ Definition of the ticket's settings""" diff --git a/tickets/preferences/views.py b/tickets/preferences/views.py index cfc6a0f2..f26473a4 100644 --- a/tickets/preferences/views.py +++ b/tickets/preferences/views.py @@ -38,11 +38,12 @@ from re2o.base import re2o_paginator from re2o.acl import can_view, can_view_all, can_edit, can_create -from preferences.utils.views import edit_options_template_function +from preferences.views import edit_options_template_function from . import forms from . import models + def aff_preferences(request): """ View to display the settings of the tickets in the preferences page""" pref, created = models.TicketOption.objects.get_or_create() @@ -54,7 +55,7 @@ def aff_preferences(request): "tickets/preferences.html", context=context, request=request, using=None ) + @login_required def edit_options(request, section): return edit_options_template_function(request, section, forms, models) - From d68a0bd627a6efab52923aa699e890a51e391112 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 22 Apr 2020 21:37:21 +0200 Subject: [PATCH 177/490] Fix and harmonize some issues in ticket app --- tickets/forms.py | 25 ++- tickets/locale/fr/LC_MESSAGES/django.po | 149 +++++++++--------- tickets/migrations/0004_auto_20200422_2127.py | 24 +++ tickets/models.py | 18 +-- tickets/preferences/forms.py | 1 - tickets/preferences/models.py | 4 - tickets/preferences/views.py | 1 - tickets/templates/tickets/aff_ticket.html | 19 +-- .../{form_preferences.html => edit.html} | 20 +-- tickets/templates/tickets/form_ticket.html | 59 ------- tickets/templates/tickets/help_text.html | 14 ++ tickets/templates/tickets/preferences.html | 6 +- tickets/templates/tickets/publication_mail_en | 4 +- tickets/templates/tickets/publication_mail_fr | 4 +- tickets/urls.py | 2 + tickets/views.py | 103 ++++++------ 16 files changed, 216 insertions(+), 237 deletions(-) create mode 100644 tickets/migrations/0004_auto_20200422_2127.py rename tickets/templates/tickets/{form_preferences.html => edit.html} (67%) delete mode 100644 tickets/templates/tickets/form_ticket.html create mode 100644 tickets/templates/tickets/help_text.html diff --git a/tickets/forms.py b/tickets/forms.py index 4f62db84..b1be6597 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -25,6 +25,7 @@ Ticket form from django import forms +from django.template.loader import render_to_string from django.forms import ModelForm, Form from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin @@ -33,19 +34,31 @@ from django.utils.translation import ugettext_lazy as _ from .models import Ticket -class NewTicketForm(ModelForm): +class NewTicketForm(FormRevMixin, ModelForm): """ Creation of a ticket""" - email = forms.EmailField(required=False) - class Meta: model = Ticket fields = ["title", "description", "email"] + def __init__(self, *args, **kwargs): + request = kwargs.pop("request") + super(NewTicketForm, self).__init__(*args, **kwargs) + if request.user.is_authenticated: + self.fields.pop('email') + self.instance.user = request.user + self.fields['description'].help_text = render_to_string('tickets/help_text.html') + self.instance.request = request -class ChangeStatusTicketForm(ModelForm): - """ Change ticket status""" + +class EditTicketForm(FormRevMixin, ModelForm): + """ Creation of a ticket""" class Meta: model = Ticket - fields = [] + fields = "__all__" + + def __init__(self, *args, **kwargs): + super(EditTicketForm, self).__init__(*args, **kwargs) + self.fields['email'].required = False + diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 528bf611..05ce5d9b 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-22 22:06+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,32 +34,28 @@ msgstr "" msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:58 -msgid "Description of the ticket." -msgstr "Description du ticket." - -#: tickets/models.py:64 +#: tickets/models.py:63 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:70 +#: tickets/models.py:69 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:71 +#: tickets/models.py:70 msgid "ticket" msgstr "ticket" -#: tickets/models.py:72 +#: tickets/models.py:71 msgid "tickets" msgstr "tickets" -#: tickets/models.py:76 +#: tickets/models.py:75 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:78 +#: tickets/models.py:77 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." @@ -76,34 +72,22 @@ msgstr "Vous n'avez pas le droit de voir la liste des tickets." msgid "Publish address" msgstr "Adresse mail de publication" -#: tickets/preferences/forms.py:45 -msgid "Mail language" -msgstr "Langue du mail" - -#: tickets/preferences/models.py:38 +#: tickets/preferences/models.py:39 msgid "" "Email address to publish the new tickets (leave empty for no publication)." msgstr "" "Adresse mail où publier les nouveaux tickets (laissez vide pour ne pas " "publier)." -#: tickets/preferences/models.py:45 -msgid "French" -msgstr "Français" - -#: tickets/preferences/models.py:45 -msgid "English" -msgstr "Anglais" - -#: tickets/preferences/models.py:49 +#: tickets/preferences/models.py:46 msgid "tickets options" msgstr "Options des tickets" -#: tickets/preferences/models.py:50 +#: tickets/preferences/models.py:47 msgid "Can view tickets options" msgstr "Peut voir les options des tickets" -#: tickets/templates/tickets/aff_ticket.html:30 +#: tickets/templates/tickets/aff_ticket.html:31 #: tickets/templates/tickets/contact.html:4 #: tickets/templates/tickets/index.html:29 #: tickets/templates/tickets/preferences.html:6 @@ -111,59 +95,62 @@ msgstr "Peut voir les options des tickets" msgid "Tickets" msgstr "Tickets" -#: tickets/templates/tickets/aff_ticket.html:34 +#: tickets/templates/tickets/aff_ticket.html:35 #, python-format msgid "Ticket #%(id)s" msgstr "Ticket #%(id)s" -#: tickets/templates/tickets/aff_ticket.html:36 +#: tickets/templates/tickets/aff_ticket.html:37 #: tickets/templates/tickets/aff_tickets.html:58 msgid "Solved" msgstr "Résolu" -#: tickets/templates/tickets/aff_ticket.html:38 +#: tickets/templates/tickets/aff_ticket.html:39 msgid "Not solved" msgstr "Non résolu" -#: tickets/templates/tickets/aff_ticket.html:44 +#: tickets/templates/tickets/aff_ticket.html:45 msgid "Opened by" msgstr "Ouvert par" -#: tickets/templates/tickets/aff_ticket.html:50 +#: tickets/templates/tickets/aff_ticket.html:51 msgid "Anonymous user" msgstr "Utilisateur anonyme" -#: tickets/templates/tickets/aff_ticket.html:54 +#: tickets/templates/tickets/aff_ticket.html:55 msgid "Response address: " msgstr "Adresse de réponse : " -#: tickets/templates/tickets/aff_ticket.html:54 +#: tickets/templates/tickets/aff_ticket.html:55 msgid "Response to your ticket" msgstr "Réponse à votre ticket" -#: tickets/templates/tickets/aff_ticket.html:59 +#: tickets/templates/tickets/aff_ticket.html:60 msgid "Title:" msgstr "Titre :" -#: tickets/templates/tickets/aff_ticket.html:60 +#: tickets/templates/tickets/aff_ticket.html:61 msgid "Description:" msgstr "Description :" -#: tickets/templates/tickets/aff_ticket.html:68 +#: tickets/templates/tickets/aff_ticket.html:65 +msgid "Edit this ticket" +msgstr "Modifier le ticket." + +#: tickets/templates/tickets/aff_ticket.html:67 msgid "Mark as solved" msgstr "Marquer comme résolu" -#: tickets/templates/tickets/aff_ticket.html:71 -msgid "Mark as not solved" +#: tickets/templates/tickets/aff_ticket.html:69 +msgid "Mark as unsolved" msgstr "Marquer comme non résolu" -#: tickets/templates/tickets/aff_ticket.html:81 +#: tickets/templates/tickets/aff_ticket.html:78 msgid "All tickets" msgstr "Tous les tickets" #: tickets/templates/tickets/aff_tickets.html:35 -#: tickets/templates/tickets/form_preferences.html:30 -#: tickets/templates/tickets/form_ticket.html:31 +#: tickets/templates/tickets/edit.html:31 msgid "Ticket" msgid_plural "Tickets" msgstr[0] "Ticket" @@ -213,28 +200,11 @@ msgstr "" msgid "Open a ticket" msgstr "Ouvrir un ticket" -#: tickets/templates/tickets/form_preferences.html:33 -msgid "Editing of tickets preferences" -msgstr "Modification des préférences de tickets" - -#: tickets/templates/tickets/form_preferences.html:46 -#: tickets/templates/tickets/preferences.html:14 -msgid "Edit" -msgstr "Modifier" - -#: tickets/templates/tickets/form_ticket.html:34 +#: tickets/templates/tickets/edit.html:34 msgid "Ticket opening" msgstr "Ouverture de ticket" -#: tickets/templates/tickets/form_ticket.html:39 tickets/views.py:85 -msgid "" -"You are not authenticated. Please log in or provide an email address so we " -"can get back to you." -msgstr "" -"Vous n'êtes pas authentifié. Veuillez vous connecter ou fournir une adresse " -"mail pour que nous puissions vous recontacter." - -#: tickets/templates/tickets/form_ticket.html:44 +#: tickets/templates/tickets/help_text.html:3 msgid "" "Description of your problem. Please give as much information as possible to " "help us searching for a solution. Here is some information we might need:" @@ -243,11 +213,11 @@ msgstr "" "possible pour nous aider à chercher une solution. Voici quelques " "informations dont nous pourrions avoir besoin :" -#: tickets/templates/tickets/form_ticket.html:47 +#: tickets/templates/tickets/help_text.html:6 msgid "The type of your problem (membership, connection, payment etc.)." msgstr "Le type de votre problème (adhésion, connexion, paiement etc.)." -#: tickets/templates/tickets/form_ticket.html:50 +#: tickets/templates/tickets/help_text.html:9 msgid "" "The conditions in which you encounter the problem (Wi-Fi/wired connection, " "on every machines or only one, on a new machine etc.)." @@ -256,7 +226,7 @@ msgstr "" "filaire, sur toutes les machines ou une seule, sur une nouvelle machine " "etc.)." -#: tickets/templates/tickets/form_ticket.html:53 +#: tickets/templates/tickets/help_text.html:12 msgid "" "The locations where you encounter the problem (in your room, in a common " "space, in a specific building etc.)." @@ -264,10 +234,6 @@ msgstr "" "Les lieux où vous rencontrez le problème (dans votre chambre, dans un espace " "commun, dans un bâtiment en particulier etc.)." -#: tickets/templates/tickets/form_ticket.html:56 -msgid "Open the ticket" -msgstr "Ouvrir le ticket" - #: tickets/templates/tickets/index.html:32 msgid "List of tickets" msgstr "Liste des tickets" @@ -276,6 +242,10 @@ msgstr "Liste des tickets" msgid "Manage the tickets" msgstr "Gérer les tickets" +#: tickets/templates/tickets/preferences.html:14 +msgid "Edit" +msgstr "Modifier" + #: tickets/templates/tickets/preferences.html:21 msgid "Publication email address" msgstr "Adresse mail de publication" @@ -284,25 +254,56 @@ msgstr "Adresse mail de publication" msgid "No email address, the tickets will not be published." msgstr "Pas d'adresse mail, les tickets ne seront pas publiés." -#: tickets/templates/tickets/preferences.html:29 -msgid "Email language" -msgstr "Langue du mail" - #: tickets/templates/tickets/profil.html:19 msgid "No tickets" msgstr "Pas de tickets" -#: tickets/views.py:66 tickets/views.py:77 +#: tickets/views.py:56 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:122 tickets/views.py:147 +#: tickets/views.py:102 +msgid "Ticket has been updated successfully" +msgstr "Le ticket a été mis à jour" + +#: tickets/views.py:123 tickets/views.py:148 msgid "Never" msgstr "Jamais" +#~ msgid "Description of the ticket." +#~ msgstr "Description du ticket." + +#~ msgid "Mail language" +#~ msgstr "Langue du mail" + +#~ msgid "French" +#~ msgstr "Français" + +#~ msgid "English" +#~ msgstr "Anglais" + +#~ msgid "Mark as not solved" +#~ msgstr "Marquer comme non résolu" + +#~ msgid "Editing of tickets preferences" +#~ msgstr "Modification des préférences de tickets" + +#~ msgid "" +#~ "You are not authenticated. Please log in or provide an email address so " +#~ "we can get back to you." +#~ msgstr "" +#~ "Vous n'êtes pas authentifié. Veuillez vous connecter ou fournir une " +#~ "adresse mail pour que nous puissions vous recontacter." + +#~ msgid "Open the ticket" +#~ msgstr "Ouvrir le ticket" + +#~ msgid "Email language" +#~ msgstr "Langue du mail" + #~ msgid "The tickets preferences were edited." #~ msgstr "Les préférences de tickets ont été modifiées." diff --git a/tickets/migrations/0004_auto_20200422_2127.py b/tickets/migrations/0004_auto_20200422_2127.py new file mode 100644 index 00000000..c1dfa3f4 --- /dev/null +++ b/tickets/migrations/0004_auto_20200422_2127.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-22 19:27 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0003_auto_20200422_1839'), + ] + + operations = [ + migrations.RemoveField( + model_name='ticketoption', + name='mail_language', + ), + migrations.AlterField( + model_name='ticket', + name='description', + field=models.TextField(max_length=3000), + ), + ] diff --git a/tickets/models.py b/tickets/models.py index bf0a3794..5cc7d003 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -31,7 +31,7 @@ from django.db.models.signals import post_save from django.dispatch import receiver from re2o.mixins import AclMixin -from re2o.mail_utils import send_mail +from django.core.mail import EmailMessage from preferences.models import GeneralOption @@ -55,7 +55,6 @@ class Ticket(AclMixin, models.Model): ) description = models.TextField( max_length=3000, - help_text=_("Description of the ticket."), blank=False, null=False, ) @@ -77,27 +76,28 @@ class Ticket(AclMixin, models.Model): else: return _("Anonymous ticket. Date: %s.") % (self.date) - def publish_mail(self, request=None): + def publish_mail(self): site_url = GeneralOption.get_cached_value("main_site_url") to_addr = TicketOption.get_cached_value("publish_address") context = {"ticket": self, "site_url": site_url} - lang = TicketOption.get_cached_value("mail_language") - if lang == 0: + language = getattr(self.request, "LANGUAGE_CODE", "en") + if language == "fr": obj = "Nouveau ticket ouvert" template = loader.get_template("tickets/publication_mail_fr") else: obj = "New ticket opened" template = loader.get_template("tickets/publication_mail_en") - send_mail( - request, + mail_to_send = EmailMessage( obj, template.render(context), GeneralOption.get_cached_value("email_from"), [to_addr], - fail_silently=False, + reply_to=[self.email], ) + mail_to_send.send(fail_silently=False) + def can_view(self, user_request, *_args, **_kwargs): """ Check that the user has the right to view the ticket @@ -137,4 +137,4 @@ def ticket_post_save(**kwargs): if kwargs["created"]: if TicketOption.get_cached_value("publish_address"): ticket = kwargs["instance"] - ticket.publish_mail(ticket.request) + ticket.publish_mail() diff --git a/tickets/preferences/forms.py b/tickets/preferences/forms.py index 05b40ec3..04845ab8 100644 --- a/tickets/preferences/forms.py +++ b/tickets/preferences/forms.py @@ -42,4 +42,3 @@ class EditTicketOptionForm(FormRevMixin, ModelForm): prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(EditTicketOptionForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields["publish_address"].label = _("Publish address") - self.fields["mail_language"].label = _("Mail language") diff --git a/tickets/preferences/models.py b/tickets/preferences/models.py index abc4d5fd..d02202f3 100644 --- a/tickets/preferences/models.py +++ b/tickets/preferences/models.py @@ -41,10 +41,6 @@ class TicketOption(AclMixin, PreferencesModel): max_length=1000, null=True, ) - LANG_FR = 0 - LANG_EN = 1 - LANGUES = ((0, _("French")), (1, _("English"))) - mail_language = models.IntegerField(choices=LANGUES, default=LANG_FR) class Meta: verbose_name = _("tickets options") diff --git a/tickets/preferences/views.py b/tickets/preferences/views.py index f26473a4..29465b1c 100644 --- a/tickets/preferences/views.py +++ b/tickets/preferences/views.py @@ -49,7 +49,6 @@ def aff_preferences(request): pref, created = models.TicketOption.objects.get_or_create() context = { "preferences": pref, - "language": str(pref.LANGUES[pref.mail_language][1]), } return render_to_string( "tickets/preferences.html", context=context, request=request, using=None diff --git a/tickets/templates/tickets/aff_ticket.html b/tickets/templates/tickets/aff_ticket.html index b66ad1c5..c4b2625d 100644 --- a/tickets/templates/tickets/aff_ticket.html +++ b/tickets/templates/tickets/aff_ticket.html @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} {% load humanize %} +{% load acl %} {% block title %}{% trans "Tickets" %}{% endblock %} @@ -57,21 +58,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% trans "Title:" %} {{ticket.title}}

-

{% trans "Description:" %} {{ ticket.description }}

- +

{% trans "Description:" %} {{ ticket.description | linebreaks }}

+
-
- {% csrf_token %} - {% bootstrap_form changestatusform %} - + {% can_edit ticket %} +

{% trans "Edit this ticket" %}

{% if not ticket.solved %} - {% trans "Mark as solved" as tr_mark_solved %} - {% bootstrap_button tr_mark_solved button_type="submit" button_class='btn-info' %} +

{% trans "Mark as solved" %}

{% else %} - {% trans "Mark as not solved" as tr_mark_not_solved %} - {% bootstrap_button tr_mark_not_solved button_type="submit" button_class='btn-warning' %} +

{% trans "Mark as unsolved" %}

{% endif %} -
+ {% acl_end %}
diff --git a/tickets/templates/tickets/form_preferences.html b/tickets/templates/tickets/edit.html similarity index 67% rename from tickets/templates/tickets/form_preferences.html rename to tickets/templates/tickets/edit.html index d5dd223b..29725fc4 100644 --- a/tickets/templates/tickets/form_preferences.html +++ b/tickets/templates/tickets/edit.html @@ -1,4 +1,4 @@ -{% extends 'machines/sidebar.html' %} +{% extends 'users/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 @@ -25,25 +25,21 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} +{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Ticket" %}{% endblock %} {% block content %} -

{% trans "Editing of tickets preferences" %}

+

{% trans "Ticket opening" %}

-{% for message in messages %} -
- - {{ message | safe }} -
-{% endfor %} +{% bootstrap_form_errors ticketform %}
{% csrf_token %} - {% bootstrap_field preferencesform.publish_address %} - {% bootstrap_field preferencesform.mail_language %} - {% trans "Edit" as tr_edit %} - {% bootstrap_button tr_edit button_type="submit" icon='ok' button_class='btn-success' %} + {% bootstrap_form ticketform %} + {% bootstrap_button action_name button_type="submit" icon='ok' button_class='btn-success' %}
+ + {% endblock %} diff --git a/tickets/templates/tickets/form_ticket.html b/tickets/templates/tickets/form_ticket.html deleted file mode 100644 index b05803bc..00000000 --- a/tickets/templates/tickets/form_ticket.html +++ /dev/null @@ -1,59 +0,0 @@ -{% extends 'users/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 Lara Kermarec -Copyright © 2017 Augustin Lemesle -Copyright © 2017 Maël Kervella - -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 massive_bootstrap_form %} -{% load i18n %} - -{% block title %}{% trans "Ticket" %}{% endblock %} - -{% block content %} -

{% trans "Ticket opening" %}

- -
- {% csrf_token %} - {% if not user.is_authenticated %} -

{% trans "You are not authenticated. Please log in or provide an email address so we can get back to you." %}

- {% bootstrap_field ticketform.email %} - {% endif %} - {% bootstrap_field ticketform.title %} -
-

{% trans "Description of your problem. Please give as much information as possible to help us searching for a solution. Here is some information we might need:" %}

-
    -
  • -

    {% trans "The type of your problem (membership, connection, payment etc.)." %}

    -
  • -
  • -

    {% trans "The conditions in which you encounter the problem (Wi-Fi/wired connection, on every machines or only one, on a new machine etc.)." %}

    -
  • -
  • -

    {% trans "The locations where you encounter the problem (in your room, in a common space, in a specific building etc.)." %}

    -
- {% bootstrap_field ticketform.description %} - {% trans "Open the ticket" as tr_open %} - {% bootstrap_button tr_open button_type="submit" icon='ok' button_class='btn-success' %} -
-{% endblock %} diff --git a/tickets/templates/tickets/help_text.html b/tickets/templates/tickets/help_text.html new file mode 100644 index 00000000..599d00f7 --- /dev/null +++ b/tickets/templates/tickets/help_text.html @@ -0,0 +1,14 @@ +{% load i18n %} +
+

{% trans "Description of your problem. Please give as much information as possible to help us searching for a solution. Here is some information we might need:" %}

+
    +
  • +

    {% trans "The type of your problem (membership, connection, payment etc.)." %}

    +
  • +
  • +

    {% trans "The conditions in which you encounter the problem (Wi-Fi/wired connection, on every machines or only one, on a new machine etc.)." %}

    +
  • +
  • +

    {% trans "The locations where you encounter the problem (in your room, in a common space, in a specific building etc.)." %}

    +
+ diff --git a/tickets/templates/tickets/preferences.html b/tickets/templates/tickets/preferences.html index 5952a496..5808ce3b 100644 --- a/tickets/templates/tickets/preferences.html +++ b/tickets/templates/tickets/preferences.html @@ -25,12 +25,8 @@

{% trans "No email address, the tickets will not be published." %}

{% endif %} - -

{% trans "Email language" %}

-

{{ language }}

-
-
+
diff --git a/tickets/templates/tickets/publication_mail_en b/tickets/templates/tickets/publication_mail_en index 5224a80b..1e436877 100644 --- a/tickets/templates/tickets/publication_mail_en +++ b/tickets/templates/tickets/publication_mail_en @@ -7,6 +7,6 @@ An anonymous user (not authenticated) opened a ticket Answer to the address:{{ticket.email}}. {% endif %} -Title: {{ticket.title}} +Title: {{ ticket.title | safe }} -Description: {{ticket.description}} +Description: {{ ticket.description | safe }} diff --git a/tickets/templates/tickets/publication_mail_fr b/tickets/templates/tickets/publication_mail_fr index ef1099da..3b733e8f 100644 --- a/tickets/templates/tickets/publication_mail_fr +++ b/tickets/templates/tickets/publication_mail_fr @@ -7,6 +7,6 @@ Un utilisateur anonyme (non connecté) a ouvert un ticket. Répondre à l'adresse : {{ticket.email}}. {% endif %} -Titre : {{ticket.title}} +Titre : {{ ticket.title | safe }} -Description : {{ticket.description}} +Description : {{ ticket.description | safe }} diff --git a/tickets/urls.py b/tickets/urls.py index c6eaefde..dee41a14 100644 --- a/tickets/urls.py +++ b/tickets/urls.py @@ -31,6 +31,8 @@ from .preferences.views import edit_options urlpatterns = [ url(r"^$", views.aff_tickets, name="aff-tickets"), url(r"^(?P[0-9]+)$", views.aff_ticket, name="aff-ticket"), + url(r"^change_ticket_status/(?P[0-9]+)$", views.change_ticket_status, name="change-ticket-status"), + url(r"^edit_ticket/(?P[0-9]+)$", views.edit_ticket, name="edit-ticket"), url( r"^edit_options/(?P
TicketOption)$", edit_options, diff --git a/tickets/views.py b/tickets/views.py index f5186cdf..f8342476 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -42,70 +42,71 @@ from preferences.models import GeneralOption from .models import Ticket -from .forms import NewTicketForm, ChangeStatusTicketForm +from .forms import NewTicketForm, EditTicketForm def new_ticket(request): """ Ticket creation view""" - ticketform = NewTicketForm(request.POST or None) - - if request.method == "POST": - ticketform = NewTicketForm(request.POST) - - if ticketform.is_valid(): - email = ticketform.cleaned_data.get("email") - ticket = ticketform.save(commit=False) - ticket.request = request - - if request.user.is_authenticated: - ticket.user = request.user - ticket.save() - messages.success( - request, - _( - "Your ticket has been succesfully opened. We will take care of it as soon as possible." - ), - ) - return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) - if not request.user.is_authenticated and email != "": - ticket.save() - messages.success( - request, - _( - "Your ticket has been succesfully opened. We will take care of it as soon as possible." - ), - ) - return redirect(reverse("index")) - else: - messages.error( - request, - _( - "You are not authenticated. Please log in or provide an email address so we can get back to you." - ), - ) - return form( - {"ticketform": ticketform}, "tickets/form_ticket.html", request - ) - - else: - ticketform = NewTicketForm - return form({"ticketform": ticketform}, "tickets/form_ticket.html", request) + ticketform = NewTicketForm(request.POST or None, request=request) + if ticketform.is_valid(): + ticketform.save() + messages.success( + request, + _( + "Your ticket has been succesfully opened. We will take care of it as soon as possible." + ), + ) + if not request.user.is_authenticated: + return redirect(reverse("index")) + else: + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + return form( + {"ticketform": ticketform, 'action_name': ("Create a ticket")}, "tickets/edit.html", request + ) @login_required @can_view(Ticket) def aff_ticket(request, ticket, ticketid): """View to display only one ticket""" - changestatusform = ChangeStatusTicketForm(request.POST) - if request.method == "POST": - ticket.solved = not ticket.solved - ticket.save() return render( request, "tickets/aff_ticket.html", - {"ticket": ticket, "changestatusform": changestatusform}, + {"ticket": ticket}, + ) + + +@login_required +@can_edit(Ticket) +def change_ticket_status(request, ticket, ticketid): + """View to edit ticket state""" + ticket.solved = not ticket.solved + ticket.save() + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + + +@login_required +@can_edit(Ticket) +def edit_ticket(request, ticket, ticketid): + """ Ticket creation view""" + ticketform = EditTicketForm(request.POST or None, instance=ticket) + if ticketform.is_valid(): + ticketform.save() + messages.success( + request, + _( + "Ticket has been updated successfully" + ), + ) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"ticketform": ticketform, 'action_name': ("Edit this ticket")}, "tickets/edit.html", request ) From 2f018cf11b34e30af1ff178ff5cc37426d1c459f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 23 Apr 2020 02:42:18 +0200 Subject: [PATCH 178/490] Add comment to tikets + send_notif mails --- tickets/admin.py | 6 +- tickets/forms.py | 17 +- tickets/locale/fr/LC_MESSAGES/django.po | 160 +++++++++++++----- tickets/migrations/0005_auto_20200422_2309.py | 40 +++++ tickets/migrations/0006_auto_20200423_0202.py | 31 ++++ tickets/migrations/0007_ticket_language.py | 20 +++ tickets/models.py | 135 ++++++++++++++- tickets/templates/tickets/aff_ticket.html | 45 +++-- tickets/templates/tickets/delete.html | 43 +++++ tickets/templates/tickets/publication_mail_en | 4 +- tickets/templates/tickets/publication_mail_fr | 4 +- tickets/templates/tickets/update_mail_en | 12 ++ tickets/templates/tickets/update_mail_fr | 12 ++ tickets/urls.py | 3 + tickets/views.py | 68 +++++++- 15 files changed, 524 insertions(+), 76 deletions(-) create mode 100644 tickets/migrations/0005_auto_20200422_2309.py create mode 100644 tickets/migrations/0006_auto_20200423_0202.py create mode 100644 tickets/migrations/0007_ticket_language.py create mode 100644 tickets/templates/tickets/delete.html create mode 100644 tickets/templates/tickets/update_mail_en create mode 100644 tickets/templates/tickets/update_mail_fr diff --git a/tickets/admin.py b/tickets/admin.py index 69f84d0d..1644ee75 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -25,13 +25,15 @@ Ticket preferences model from django.contrib import admin -from .models import Ticket +from .models import Ticket, CommentTicket from reversion.admin import VersionAdmin class TicketAdmin(VersionAdmin): - """Gestion des ticket""" + pass +class CommentTicketAdmin(VersionAdmin): pass admin.site.register(Ticket, TicketAdmin) +admin.site.register(CommentTicket, CommentTicketAdmin) diff --git a/tickets/forms.py b/tickets/forms.py index b1be6597..a9f6617d 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -31,7 +31,7 @@ from re2o.field_permissions import FieldPermissionFormMixin from re2o.mixins import FormRevMixin from django.utils.translation import ugettext_lazy as _ -from .models import Ticket +from .models import Ticket, CommentTicket class NewTicketForm(FormRevMixin, ModelForm): @@ -48,7 +48,7 @@ class NewTicketForm(FormRevMixin, ModelForm): self.fields.pop('email') self.instance.user = request.user self.fields['description'].help_text = render_to_string('tickets/help_text.html') - self.instance.request = request + self.instance.language = getattr(request, "LANGUAGE_CODE", "en") class EditTicketForm(FormRevMixin, ModelForm): @@ -62,3 +62,16 @@ class EditTicketForm(FormRevMixin, ModelForm): super(EditTicketForm, self).__init__(*args, **kwargs) self.fields['email'].required = False + +class CommentTicketForm(FormRevMixin, ModelForm): + """Edit and create comment to a ticket""" + + class Meta: + model = CommentTicket + fields = ["comment"] + + def __init__(self, *args, **kwargs): + prefix = kwargs.pop("prefix", self.Meta.model.__name__) + super(CommentTicketForm, self).__init__(*args, prefix=prefix, **kwargs) + self.fields["comment"].label = _("comment") + diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 05ce5d9b..415c618f 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 22:06+0200\n" +"POT-Creation-Date: 2020-04-23 03:10+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,44 +30,68 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/models.py:54 +#: tickets/forms.py:76 +msgid "comment" +msgstr "commentaire" + +#: tickets/models.py:57 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:63 +#: tickets/models.py:66 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:69 +#: tickets/models.py:70 +msgid "Language of the ticket." +msgstr "Langue des tickets" + +#: tickets/models.py:74 tickets/models.py:170 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:70 +#: tickets/models.py:75 tickets/models.py:171 msgid "ticket" msgstr "ticket" -#: tickets/models.py:71 +#: tickets/models.py:76 tickets/models.py:172 msgid "tickets" msgstr "tickets" -#: tickets/models.py:75 +#: tickets/models.py:80 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:77 +#: tickets/models.py:82 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:111 +#: tickets/models.py:90 tickets/templates/tickets/aff_ticket.html:52 +msgid "Anonymous user" +msgstr "Utilisateur anonyme" + +#: tickets/models.py:128 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:123 +#: tickets/models.py:140 tickets/models.py:214 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." +#: tickets/models.py:187 +msgid "You don't have the right to view other tickets comments than yours." +msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." + +#: tickets/models.py:202 +msgid "You don't have the right to edit other tickets comments than yours." +msgstr "Vous n'avez pas le droit d'éditer d'autres tickets que les vôtres." + +#: tickets/models.py:232 +msgid "Update of your ticket" +msgstr "Mise à jour de votre ticket" + #: tickets/preferences/forms.py:44 msgid "Publish address" msgstr "Adresse mail de publication" @@ -87,7 +111,7 @@ msgstr "Options des tickets" msgid "Can view tickets options" msgstr "Peut voir les options des tickets" -#: tickets/templates/tickets/aff_ticket.html:31 +#: tickets/templates/tickets/aff_ticket.html:32 #: tickets/templates/tickets/contact.html:4 #: tickets/templates/tickets/index.html:29 #: tickets/templates/tickets/preferences.html:6 @@ -95,57 +119,79 @@ msgstr "Peut voir les options des tickets" msgid "Tickets" msgstr "Tickets" -#: tickets/templates/tickets/aff_ticket.html:35 +#: tickets/templates/tickets/aff_ticket.html:36 #, python-format msgid "Ticket #%(id)s" msgstr "Ticket #%(id)s" -#: tickets/templates/tickets/aff_ticket.html:37 +#: tickets/templates/tickets/aff_ticket.html:38 #: tickets/templates/tickets/aff_tickets.html:58 msgid "Solved" msgstr "Résolu" -#: tickets/templates/tickets/aff_ticket.html:39 +#: tickets/templates/tickets/aff_ticket.html:40 msgid "Not solved" msgstr "Non résolu" -#: tickets/templates/tickets/aff_ticket.html:45 +#: tickets/templates/tickets/aff_ticket.html:46 msgid "Opened by" msgstr "Ouvert par" -#: tickets/templates/tickets/aff_ticket.html:51 -msgid "Anonymous user" -msgstr "Utilisateur anonyme" - -#: tickets/templates/tickets/aff_ticket.html:55 +#: tickets/templates/tickets/aff_ticket.html:56 msgid "Response address: " msgstr "Adresse de réponse : " -#: tickets/templates/tickets/aff_ticket.html:55 +#: tickets/templates/tickets/aff_ticket.html:56 msgid "Response to your ticket" msgstr "Réponse à votre ticket" -#: tickets/templates/tickets/aff_ticket.html:60 -msgid "Title:" -msgstr "Titre :" - #: tickets/templates/tickets/aff_ticket.html:61 -msgid "Description:" -msgstr "Description :" +msgid "Add a comment " +msgstr "Ajouter un commentaire" -#: tickets/templates/tickets/aff_ticket.html:65 -msgid "Edit this ticket" -msgstr "Modifier le ticket." +#: tickets/templates/tickets/aff_ticket.html:64 +#: tickets/templates/tickets/preferences.html:14 tickets/views.py:153 +msgid "Edit" +msgstr "Modifier" -#: tickets/templates/tickets/aff_ticket.html:67 +#: tickets/templates/tickets/aff_ticket.html:66 msgid "Mark as solved" msgstr "Marquer comme résolu" -#: tickets/templates/tickets/aff_ticket.html:69 +#: tickets/templates/tickets/aff_ticket.html:68 msgid "Mark as unsolved" msgstr "Marquer comme non résolu" -#: tickets/templates/tickets/aff_ticket.html:78 +#: tickets/templates/tickets/aff_ticket.html:76 +msgid "Title:" +msgstr "Titre :" + +#: tickets/templates/tickets/aff_ticket.html:77 +#: tickets/templates/tickets/aff_ticket.html:84 +msgid "Description:" +msgstr "Description :" + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid "Comment " +msgstr "Commentaire" + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid " added by " +msgstr " ajouté par " + +#: tickets/templates/tickets/aff_ticket.html:83 +msgid " on " +msgstr " le " + +#: tickets/templates/tickets/aff_ticket.html:87 +msgid "Edit this comment " +msgstr "Modifier le commentaire" + +#: tickets/templates/tickets/aff_ticket.html:90 +msgid "Delete this comment " +msgstr "Supprimer ce commentaire" + +#: tickets/templates/tickets/aff_ticket.html:99 msgid "All tickets" msgstr "Tous les tickets" @@ -200,6 +246,21 @@ msgstr "" msgid "Open a ticket" msgstr "Ouvrir un ticket" +#: tickets/templates/tickets/delete.html:29 +msgid "Deletion of tickets" +msgstr "Suppression de tickets" + +#: tickets/templates/tickets/delete.html:35 +#, python-format +msgid "" +"Warning: are you sure you want to delete this %(objet_name)s object " +"( %(objet)s )?" +msgstr "" + +#: tickets/templates/tickets/delete.html:36 +msgid "Confirm" +msgstr "Confirmer" + #: tickets/templates/tickets/edit.html:34 msgid "Ticket opening" msgstr "Ouverture de ticket" @@ -242,10 +303,6 @@ msgstr "Liste des tickets" msgid "Manage the tickets" msgstr "Gérer les tickets" -#: tickets/templates/tickets/preferences.html:14 -msgid "Edit" -msgstr "Modifier" - #: tickets/templates/tickets/preferences.html:21 msgid "Publication email address" msgstr "Adresse mail de publication" @@ -258,18 +315,38 @@ msgstr "Pas d'adresse mail, les tickets ne seront pas publiés." msgid "No tickets" msgstr "Pas de tickets" -#: tickets/views.py:56 +#: tickets/views.py:62 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:102 +#: tickets/views.py:109 msgid "Ticket has been updated successfully" msgstr "Le ticket a été mis à jour" -#: tickets/views.py:123 tickets/views.py:148 +#: tickets/views.py:130 +msgid "This comment was added." +msgstr "Le commentaire a été ajouté" + +#: tickets/views.py:135 +msgid "Add a comment" +msgstr "Ajouter un commentaire" + +#: tickets/views.py:148 +msgid "This comment was edited." +msgstr "Le commentaire a été édité" + +#: tickets/views.py:164 +msgid "The comment was deleted." +msgstr "Le commentaire a été supprimé" + +#: tickets/views.py:169 +msgid "Ticket Comment" +msgstr "Commentaire de ticket" + +#: tickets/views.py:183 tickets/views.py:208 msgid "Never" msgstr "Jamais" @@ -298,9 +375,6 @@ msgstr "Jamais" #~ "Vous n'êtes pas authentifié. Veuillez vous connecter ou fournir une " #~ "adresse mail pour que nous puissions vous recontacter." -#~ msgid "Open the ticket" -#~ msgstr "Ouvrir le ticket" - #~ msgid "Email language" #~ msgstr "Langue du mail" diff --git a/tickets/migrations/0005_auto_20200422_2309.py b/tickets/migrations/0005_auto_20200422_2309.py new file mode 100644 index 00000000..43fdbdb3 --- /dev/null +++ b/tickets/migrations/0005_auto_20200422_2309.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-22 21:09 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import re2o.mixins + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0004_auto_20200422_2127'), + ] + + operations = [ + migrations.CreateModel( + name='CommentTicket', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('comment', models.TextField(max_length=4095)), + ], + options={ + 'verbose_name': 'ticket', + 'verbose_name_plural': 'tickets', + 'permissions': (('view_commentticket', 'Can view a ticket object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AlterModelOptions( + name='ticket', + options={'permissions': (('view_ticket', 'Can view a ticket object'),), 'verbose_name': 'ticket', 'verbose_name_plural': 'tickets'}, + ), + migrations.AddField( + model_name='commentticket', + name='parent_ticket', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.Ticket'), + ), + ] diff --git a/tickets/migrations/0006_auto_20200423_0202.py b/tickets/migrations/0006_auto_20200423_0202.py new file mode 100644 index 00000000..565a8b7a --- /dev/null +++ b/tickets/migrations/0006_auto_20200423_0202.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 00:02 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('tickets', '0005_auto_20200422_2309'), + ] + + operations = [ + migrations.AddField( + model_name='commentticket', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='commentticket', + name='created_by', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_comment', to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] diff --git a/tickets/migrations/0007_ticket_language.py b/tickets/migrations/0007_ticket_language.py new file mode 100644 index 00000000..351d2359 --- /dev/null +++ b/tickets/migrations/0007_ticket_language.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 01:05 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0006_auto_20200423_0202'), + ] + + operations = [ + migrations.AddField( + model_name='ticket', + name='language', + field=models.CharField(default='en', help_text='Language of the ticket.', max_length=16), + ), + ] diff --git a/tickets/models.py b/tickets/models.py index 5cc7d003..67bf8f34 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -29,6 +29,9 @@ from django.utils.translation import ugettext_lazy as _ from django.template import loader from django.db.models.signals import post_save from django.dispatch import receiver +from django.utils.functional import cached_property + +from reversion.models import Version from re2o.mixins import AclMixin from django.core.mail import EmailMessage @@ -63,10 +66,12 @@ class Ticket(AclMixin, models.Model): help_text=_("An email address to get back to you."), max_length=100, null=True ) solved = models.BooleanField(default=False) - request = None + language = models.CharField( + max_length=16, help_text=_("Language of the ticket."), default="en" + ) class Meta: - permissions = (("view_tickets", _("Can view a ticket object")),) + permissions = (("view_ticket", _("Can view a ticket object")),) verbose_name = _("ticket") verbose_name_plural = _("tickets") @@ -76,13 +81,25 @@ class Ticket(AclMixin, models.Model): else: return _("Anonymous ticket. Date: %s.") % (self.date) + @cached_property + def opened_by(self): + """Return full name of this ticket opener""" + if self.user: + return self.user.get_full_name() + else: + return _("Anonymous user") + + @cached_property + def get_mail(self): + """Return the mail of the owner of this ticket""" + return self.email or self.user.get_mail + def publish_mail(self): site_url = GeneralOption.get_cached_value("main_site_url") to_addr = TicketOption.get_cached_value("publish_address") context = {"ticket": self, "site_url": site_url} - language = getattr(self.request, "LANGUAGE_CODE", "en") - if language == "fr": + if self.language == "fr": obj = "Nouveau ticket ouvert" template = loader.get_template("tickets/publication_mail_fr") else: @@ -94,7 +111,7 @@ class Ticket(AclMixin, models.Model): template.render(context), GeneralOption.get_cached_value("email_from"), [to_addr], - reply_to=[self.email], + reply_to=[self.get_mail], ) mail_to_send.send(fail_silently=False) @@ -103,13 +120,13 @@ class Ticket(AclMixin, models.Model): """ Check that the user has the right to view the ticket or that it is the author""" if ( - not user_request.has_perm("tickets.view_tickets") + not user_request.has_perm("tickets.view_ticket") and self.user != user_request ): return ( False, _("You don't have the right to view other tickets than yours."), - ("tickets.view_tickets",), + ("tickets.view_ticket",), ) else: return True, None, None @@ -117,13 +134,13 @@ class Ticket(AclMixin, models.Model): @staticmethod def can_view_all(user_request, *_args, **_kwargs): """ Check that the user has access to the list of all tickets""" - can = user_request.has_perm("tickets.view_tickets") + can = user_request.has_perm("tickets.view_ticket") return ( can, _("You don't have the right to view the list of tickets.") if not can else None, - ("tickets.view_tickets",), + ("tickets.view_ticket",), ) def can_create(user_request, *_args, **_kwargs): @@ -131,6 +148,97 @@ class Ticket(AclMixin, models.Model): return True, None, None +class CommentTicket(AclMixin, models.Model): + """A comment of a ticket""" + date = models.DateTimeField(auto_now_add=True) + comment = models.TextField( + max_length=4095, + blank=False, + null=False, + ) + parent_ticket = models.ForeignKey( + "Ticket", on_delete=models.CASCADE + ) + created_at = models.DateTimeField(auto_now_add=True) + created_by = models.ForeignKey( + "users.User", + on_delete=models.CASCADE, + related_name="ticket_comment", + ) + + class Meta: + permissions = (("view_commentticket", _("Can view a ticket object")),) + verbose_name = _("ticket") + verbose_name_plural = _("tickets") + + @cached_property + def comment_id(self): + return CommentTicket.objects.filter(parent_ticket=self.parent_ticket, pk__lt=self.pk).count() + 1 + + def can_view(self, user_request, *_args, **_kwargs): + """ Check that the user has the right to view the ticket comment + or that it is the author""" + if ( + not user_request.has_perm("tickets.view_commentticket") + and self.parent_ticket.user != user_request + ): + return ( + False, + _("You don't have the right to view other tickets comments than yours."), + ("tickets.view_commentticket",), + ) + else: + return True, None, None + + def can_edit(self, user_request, *_args, **_kwargs): + """ Check that the user has the right to edit the ticket comment + or that it is the author""" + if ( + not user_request.has_perm("tickets.edit_commentticket") + and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by) + ): + return ( + False, + _("You don't have the right to edit other tickets comments than yours."), + ("tickets.edit_commentticket",), + ) + else: + return True, None, None + + @staticmethod + def can_view_all(user_request, *_args, **_kwargs): + """ Check that the user has access to the list of all tickets comments""" + can = user_request.has_perm("tickets.view_commentticket") + return ( + can, + _("You don't have the right to view the list of tickets.") + if not can + else None, + ("tickets.view_commentticket",), + ) + + def __str__(self): + return "Comment " + str(self.comment_id) + " on " + str(self.parent_ticket) + + def publish_mail(self): + site_url = GeneralOption.get_cached_value("main_site_url") + to_addr = TicketOption.get_cached_value("publish_address") + context = {"comment": self, "site_url": site_url} + + if self.parent_ticket.language == "fr": + template = loader.get_template("tickets/update_mail_fr") + else: + template = loader.get_template("tickets/update_mail_en") + obj = _("Update of your ticket") + mail_to_send = EmailMessage( + obj, + template.render(context), + GeneralOption.get_cached_value("email_from"), + [to_addr, self.parent_ticket.get_mail], + ) + mail_to_send.send(fail_silently=False) + + @receiver(post_save, sender=Ticket) def ticket_post_save(**kwargs): """ Send the mail to publish the new ticket """ @@ -138,3 +246,12 @@ def ticket_post_save(**kwargs): if TicketOption.get_cached_value("publish_address"): ticket = kwargs["instance"] ticket.publish_mail() + + +@receiver(post_save, sender=CommentTicket) +def comment_post_save(**kwargs): + """ Send the mail to publish the new comment """ + if kwargs["created"]: + if TicketOption.get_cached_value("publish_address"): + comment = kwargs["instance"] + comment.publish_mail() diff --git a/tickets/templates/tickets/aff_ticket.html b/tickets/templates/tickets/aff_ticket.html index c4b2625d..da7d0c39 100644 --- a/tickets/templates/tickets/aff_ticket.html +++ b/tickets/templates/tickets/aff_ticket.html @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} {% load humanize %} +{% load logs_extra %} {% load acl %} {% block title %}{% trans "Tickets" %}{% endblock %} @@ -54,28 +55,48 @@ with this program; if not, write to the Free Software Foundation, Inc., {% if not ticket.user %} {% trans "Response address: " %}{{ticket.email}} {% endif %} + +
+ {% can_view ticket %} + {% trans "Add a comment " %} + {% acl_end %} + {% can_edit ticket %} + {% trans "Edit" %} + {% if not ticket.solved %} + {% trans "Mark as solved" %} + {% else %} + {% trans "Mark as unsolved" %} + {% endif %} + {% acl_end %} + {% history_button ticket text=True %} +

{% trans "Title:" %} {{ticket.title}}

-

{% trans "Description:" %} {{ ticket.description | linebreaks }}

+ {% trans "Description:" %} {{ ticket.description | linebreaks }} -
- {% can_edit ticket %} -

{% trans "Edit this ticket" %}

- {% if not ticket.solved %} -

{% trans "Mark as solved" %}

- {% else %} -

{% trans "Mark as unsolved" %}

- {% endif %} +
+ +{% for comment in comments %} + +{% endfor %}
- - {% endblock %} diff --git a/tickets/templates/tickets/delete.html b/tickets/templates/tickets/delete.html new file mode 100644 index 00000000..c583ee5b --- /dev/null +++ b/tickets/templates/tickets/delete.html @@ -0,0 +1,43 @@ +{% extends 'users/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 Lara 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 "Deletion of tickets" %}{% endblock %} + +{% block content %} + +
+ {% csrf_token %} +

{% blocktrans %}Warning: are you sure you want to delete this {{ objet_name }} object ( {{ objet }} )?{% endblocktrans %}

+ {% trans "Confirm" as tr_confirm %} + {% bootstrap_button tr_confirm button_type="submit" icon="trash" button_class='btn-danger' %} +
+
+
+
+{% endblock %} + diff --git a/tickets/templates/tickets/publication_mail_en b/tickets/templates/tickets/publication_mail_en index 1e436877..97ad4b2c 100644 --- a/tickets/templates/tickets/publication_mail_en +++ b/tickets/templates/tickets/publication_mail_en @@ -1,12 +1,12 @@ {% if ticket.user %} {{ ticket.user.get_full_name }} opened a ticket. Profile: {{site_url}}{% url 'users:profil' ticket.user.id%} -Answer to the address: {{ticket.user.get_mail}}. {% else %} An anonymous user (not authenticated) opened a ticket -Answer to the address:{{ticket.email}}. {% endif %} +Answer to the address: {{{ticket.get_mail}}. Title: {{ ticket.title | safe }} Description: {{ ticket.description | safe }} + diff --git a/tickets/templates/tickets/publication_mail_fr b/tickets/templates/tickets/publication_mail_fr index 3b733e8f..3491a8d8 100644 --- a/tickets/templates/tickets/publication_mail_fr +++ b/tickets/templates/tickets/publication_mail_fr @@ -1,11 +1,11 @@ {% if ticket.user %} {{ ticket.user.get_full_name }} a ouvert un ticket. Profil : {{site_url}}{% url 'users:profil' ticket.user.id%} -Répondre à l'adresse : {{ticket.user.get_mail}}. {% else %} Un utilisateur anonyme (non connecté) a ouvert un ticket. -Répondre à l'adresse : {{ticket.email}}. {% endif %} +Répondre à l'adresse : {{ticket.get_mail}}. + Titre : {{ ticket.title | safe }} diff --git a/tickets/templates/tickets/update_mail_en b/tickets/templates/tickets/update_mail_en new file mode 100644 index 00000000..8c25d4fd --- /dev/null +++ b/tickets/templates/tickets/update_mail_en @@ -0,0 +1,12 @@ +Hello, + +The ticket {{ comment.parent_ticket.title | safe }} n°{{ comment.parent_ticket.id }}, opened by {{ comment.parent_ticket.opened_by }}, has been updated by {{ comment.created_by.get_full_name | safe }}. +{% if comment.parent_ticket.user %} +The complete re2o profil can be found here : {{site_url}}{% url 'users:profil' comment.parent_ticket.user.id%} +{% endif %} + +Description : {{ comment.comment | safe }} + +Best regards, + +The member of the association diff --git a/tickets/templates/tickets/update_mail_fr b/tickets/templates/tickets/update_mail_fr new file mode 100644 index 00000000..3b1ec8ee --- /dev/null +++ b/tickets/templates/tickets/update_mail_fr @@ -0,0 +1,12 @@ +Bonjour, + +Le ticket {{ comment.parent_ticket.title | safe }} n°{{ comment.parent_ticket.id }}, ouvert par {{ comment.parent_ticket.opened_by }}, a reçu une mise à jour par {{ comment.created_by.get_full_name | safe }}. +{% if comment.parent_ticket.user %} +Le profil re2o est accessible à l'adresse suivante : {{site_url}}{% url 'users:profil' comment.parent_ticket.user.id%} +{% endif %} + +Description : {{ comment.comment | safe }} + +Cordialement, + +Les membres actifs de l'association diff --git a/tickets/urls.py b/tickets/urls.py index dee41a14..fe0b88ec 100644 --- a/tickets/urls.py +++ b/tickets/urls.py @@ -39,4 +39,7 @@ urlpatterns = [ name="edit-options", ), url(r"^new_ticket/$", views.new_ticket, name="new-ticket"), + url(r"^add_comment/(?P[0-9]+)$", views.add_comment, name="add-comment"), + url(r"^edit_comment/(?P[0-9]+)$", views.edit_comment, name="edit-comment"), + url(r"^del_comment/(?P[0-9]+)$", views.del_comment, name="del-comment"), ] diff --git a/tickets/views.py b/tickets/views.py index f8342476..bda14df7 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -36,13 +36,19 @@ from re2o.views import form from re2o.base import re2o_paginator -from re2o.acl import can_view, can_view_all, can_edit, can_create +from re2o.acl import ( + can_view, + can_view_all, + can_edit, + can_create, + can_delete +) from preferences.models import GeneralOption -from .models import Ticket +from .models import Ticket, CommentTicket -from .forms import NewTicketForm, EditTicketForm +from .forms import NewTicketForm, EditTicketForm, CommentTicketForm def new_ticket(request): @@ -71,10 +77,11 @@ def new_ticket(request): @can_view(Ticket) def aff_ticket(request, ticket, ticketid): """View to display only one ticket""" + comments = CommentTicket.objects.filter(parent_ticket=ticket) return render( request, "tickets/aff_ticket.html", - {"ticket": ticket}, + {"ticket": ticket, "comments": comments}, ) @@ -110,6 +117,59 @@ def edit_ticket(request, ticket, ticketid): ) +@login_required +@can_view(Ticket) +def add_comment(request, ticket, ticketid): + """ Add a comment to a ticket""" + commentticket = CommentTicketForm(request.POST or None) + if commentticket.is_valid(): + commentticket = commentticket.save(commit=False) + commentticket.parent_ticket = ticket + commentticket.created_by = request.user + commentticket.save() + messages.success(request, _("This comment was added.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"ticketform": commentticket, "action_name": _("Add a comment")}, "tickets/edit.html", request + ) + + +@login_required +@can_edit(CommentTicket) +def edit_comment(request, commentticket_instance, **_kwargs): + """ Edit a comment of a ticket""" + commentticket = CommentTicketForm(request.POST or None, instance=commentticket_instance) + if commentticket.is_valid(): + ticketid = commentticket_instance.parent_ticket.id + if commentticket.changed_data: + commentticket.save() + messages.success(request, _("This comment was edited.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"ticketform": commentticket, "action_name": _("Edit")}, "tickets/edit.html", request, + ) + + +@login_required +@can_delete(CommentTicket) +def del_comment(request, commentticket, **_kwargs): + """Delete a comment of a ticket""" + if request.method == "POST": + ticketid = commentticket.parent_ticket.id + commentticket.delete() + messages.success(request, _("The comment was deleted.")) + return redirect( + reverse("tickets:aff-ticket", kwargs={"ticketid": str(ticketid)}) + ) + return form( + {"objet": commentticket, "objet_name": _("Ticket Comment")}, "tickets/delete.html", request + ) + + @login_required @can_view_all(Ticket) def aff_tickets(request): From bd9a227caed4ca3a189218a680d50688cae15ff9 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 23 Apr 2020 03:21:11 +0200 Subject: [PATCH 179/490] Lerennais remarques --- preferences/views.py | 4 ++-- tickets/forms.py | 5 ++++- tickets/models.py | 8 ++++++-- tickets/preferences/views.py | 3 --- tickets/views.py | 2 +- 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/preferences/views.py b/preferences/views.py index 6420dad3..25e7c009 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -96,8 +96,8 @@ def edit_options_template_function(request, section, forms, models): return redirect(reverse("preferences:display-options")) options_instance, _created = model.objects.get_or_create() - can, msg, permissions = options_instance.can_edit(request.user) - if not can: + _is_allowed_to_edit, msg, permissions = options_instance.can_edit(request.user) + if not _is_allowed_to_edit: messages.error(request, acl_error_message(msg, permissions)) return redirect(reverse("index")) options = form_instance( diff --git a/tickets/forms.py b/tickets/forms.py index a9f6617d..3001de8a 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -42,13 +42,14 @@ class NewTicketForm(FormRevMixin, ModelForm): fields = ["title", "description", "email"] def __init__(self, *args, **kwargs): - request = kwargs.pop("request") + request = kwargs.pop("request", None) super(NewTicketForm, self).__init__(*args, **kwargs) if request.user.is_authenticated: self.fields.pop('email') self.instance.user = request.user self.fields['description'].help_text = render_to_string('tickets/help_text.html') self.instance.language = getattr(request, "LANGUAGE_CODE", "en") + self.instance.request = request class EditTicketForm(FormRevMixin, ModelForm): @@ -71,7 +72,9 @@ class CommentTicketForm(FormRevMixin, ModelForm): fields = ["comment"] def __init__(self, *args, **kwargs): + request = kwargs.pop("request", None) prefix = kwargs.pop("prefix", self.Meta.model.__name__) super(CommentTicketForm, self).__init__(*args, prefix=prefix, **kwargs) self.fields["comment"].label = _("comment") + self.instance.request = request diff --git a/tickets/models.py b/tickets/models.py index 67bf8f34..98a006ac 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -34,6 +34,7 @@ from django.utils.functional import cached_property from reversion.models import Version from re2o.mixins import AclMixin +from re2o.mail_utils import send_mail_object from django.core.mail import EmailMessage from preferences.models import GeneralOption @@ -69,6 +70,7 @@ class Ticket(AclMixin, models.Model): language = models.CharField( max_length=16, help_text=_("Language of the ticket."), default="en" ) + request = None class Meta: permissions = (("view_ticket", _("Can view a ticket object")),) @@ -113,7 +115,7 @@ class Ticket(AclMixin, models.Model): [to_addr], reply_to=[self.get_mail], ) - mail_to_send.send(fail_silently=False) + send_mail_object(mail_to_send, self.request) def can_view(self, user_request, *_args, **_kwargs): @@ -165,6 +167,7 @@ class CommentTicket(AclMixin, models.Model): on_delete=models.CASCADE, related_name="ticket_comment", ) + request = None class Meta: permissions = (("view_commentticket", _("Can view a ticket object")),) @@ -221,6 +224,7 @@ class CommentTicket(AclMixin, models.Model): return "Comment " + str(self.comment_id) + " on " + str(self.parent_ticket) def publish_mail(self): + """Send mail to user and admin after new comment""" site_url = GeneralOption.get_cached_value("main_site_url") to_addr = TicketOption.get_cached_value("publish_address") context = {"comment": self, "site_url": site_url} @@ -236,7 +240,7 @@ class CommentTicket(AclMixin, models.Model): GeneralOption.get_cached_value("email_from"), [to_addr, self.parent_ticket.get_mail], ) - mail_to_send.send(fail_silently=False) + send_mail_object(mail_to_send, self.request) @receiver(post_save, sender=Ticket) diff --git a/tickets/preferences/views.py b/tickets/preferences/views.py index 29465b1c..70637db8 100644 --- a/tickets/preferences/views.py +++ b/tickets/preferences/views.py @@ -28,11 +28,8 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect from django.template.loader import render_to_string -from django.views.decorators.cache import cache_page from django.utils.translation import ugettext as _ from django.urls import reverse -from django.forms import modelformset_factory -from re2o.views import form from re2o.base import re2o_paginator diff --git a/tickets/views.py b/tickets/views.py index bda14df7..aab6aeae 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -121,7 +121,7 @@ def edit_ticket(request, ticket, ticketid): @can_view(Ticket) def add_comment(request, ticket, ticketid): """ Add a comment to a ticket""" - commentticket = CommentTicketForm(request.POST or None) + commentticket = CommentTicketForm(request.POST or None, request=request) if commentticket.is_valid(): commentticket = commentticket.save(commit=False) commentticket.parent_ticket = ticket From bcb9e097ac1080996a26310d8af3b68d8444bd9a Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 23 Apr 2020 12:07:15 +0200 Subject: [PATCH 180/490] Add self pseudo setting --- preferences/forms.py | 15 +- preferences/locale/fr/LC_MESSAGES/django.po | 663 ++++++++++-------- .../0071_optionaluser_self_change_pseudo.py | 20 + preferences/models.py | 3 + .../preferences/display_preferences.html | 2 + users/models.py | 23 + 6 files changed, 412 insertions(+), 314 deletions(-) create mode 100644 preferences/migrations/0071_optionaluser_self_change_pseudo.py diff --git a/preferences/forms.py b/preferences/forms.py index b296c0d4..218a26b0 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -66,10 +66,19 @@ class EditOptionalUserForm(ModelForm): self.fields["gpg_fingerprint"].label = _("GPG fingerprint") self.fields["all_can_create_club"].label = _("All can create a club") self.fields["all_can_create_adherent"].label = _("All can create a member") - self.fields["disable_emailnotyetconfirmed"].label = _("Delay before disabling accounts without a verified email") - self.fields["self_adhesion"].label = _("Self registration") self.fields["shell_default"].label = _("Default shell") - self.fields["allow_set_password_during_user_creation"].label = _("Allow directly setting a password during account creation") + self.fields["self_change_shell"].label = _("Self change shell") + self.fields["self_change_pseudo"].label = _("Self change pseudo") + self.fields["self_room_policy"].label = _("Self room policy") + self.fields["local_email_accounts_enabled"].label = _("Local email accounts enabled") + self.fields["local_email_domain"].label = _("Local email domain") + self.fields["max_email_address"].label = _("Max local email address") + self.fields["delete_notyetactive"].label = _("Delete not yet active users") + self.fields["disable_emailnotyetconfirmed"].label = _("Disabled email not yet confirmed") + self.fields["self_adhesion"].label = _("Self registration") + self.fields["all_users_active"].label = _("All users are state active by default") + self.fields["allow_set_password_during_user_creation"].label = _("Allow set password during user creation") + self.fields["allow_archived_connexion"].label = _("Allow archived connexion") class EditOptionalMachineForm(ModelForm): diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index d034ac9b..dc41163f 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 11:55+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -52,261 +52,303 @@ msgid "All can create a member" msgstr "Tous peuvent créer un adhérent" #: preferences/forms.py:69 -#: preferences/templates/preferences/display_preferences.html:135 -msgid "Delay before disabling accounts without a verified email" -msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" +msgid "Default shell" +msgstr "Interface en ligne de commande par défaut" #: preferences/forms.py:70 +msgid "Self change shell" +msgstr "Automodification du shell" + +#: preferences/forms.py:71 +msgid "Self change pseudo" +msgstr "Automodification du pseudo" + +#: preferences/forms.py:72 +msgid "Self room policy" +msgstr "Automodification de la chambre" + +#: preferences/forms.py:73 +#: preferences/templates/preferences/display_preferences.html:84 +msgid "Local email accounts enabled" +msgstr "Comptes mail locaux activés" + +#: preferences/forms.py:74 +#: preferences/templates/preferences/display_preferences.html:86 +#: preferences/templates/preferences/display_preferences.html:166 +msgid "Local email domain" +msgstr "Domaine de mail local" + +#: preferences/forms.py:75 +msgid "Max local email address" +msgstr "Maximum de mail local autorisés" + +#: preferences/forms.py:76 +msgid "Delete not yet active users" +msgstr "Suppression des utilisateurs n'ayant jamais adhéré après" + +#: preferences/forms.py:77 +msgid "Disabled email not yet confirmed" +msgstr "%(disable_emailnotyetconfirmed)s jours" + +#: preferences/forms.py:78 #: preferences/templates/preferences/display_preferences.html:121 msgid "Self registration" msgstr "Autoinscription" -#: preferences/forms.py:71 -msgid "Default shell" -msgstr "Interface en ligne de commande par défaut" +#: preferences/forms.py:79 +msgid "All users are state active by default" +msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/forms.py:72 -msgid "Allow directly setting a password during account creation" +#: preferences/forms.py:80 +msgid "Allow set password during user creation" msgstr "" "Permettre le choix d'un mot de passe directement lors de la création du " "compte" -#: preferences/forms.py:86 +#: preferences/forms.py:81 +msgid "Allow archived connexion" +msgstr "Autoriser les utilisateurs archivés à se connecter" + +#: preferences/forms.py:95 msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" -#: preferences/forms.py:89 -#: preferences/templates/preferences/display_preferences.html:194 +#: preferences/forms.py:98 +#: preferences/templates/preferences/display_preferences.html:196 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" -#: preferences/forms.py:92 -#: preferences/templates/preferences/display_preferences.html:198 +#: preferences/forms.py:101 +#: preferences/templates/preferences/display_preferences.html:200 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" -#: preferences/forms.py:94 +#: preferences/forms.py:103 msgid "IPv6 mode" msgstr "Mode IPv6" -#: preferences/forms.py:95 +#: preferences/forms.py:104 msgid "Can create a machine" msgstr "Peut créer une machine" -#: preferences/forms.py:138 +#: preferences/forms.py:147 msgid "General message in French" msgstr "Message général en français" -#: preferences/forms.py:139 +#: preferences/forms.py:148 msgid "General message in English" msgstr "Message général en anglais" -#: preferences/forms.py:141 +#: preferences/forms.py:150 #: preferences/templates/preferences/display_preferences.html:58 msgid "Number of results displayed when searching" msgstr "Nombre de résultats affichés lors de la recherche" -#: preferences/forms.py:144 +#: preferences/forms.py:153 msgid "Number of items per page, standard size (e.g. users)" msgstr "Nombre d'éléments par page, taille standard (ex : utilisateurs)" -#: preferences/forms.py:147 +#: preferences/forms.py:156 msgid "Number of items per page, large size (e.g. machines)" msgstr "Nombre d'éléments par page, taille importante (ex : machines)" -#: preferences/forms.py:150 +#: preferences/forms.py:159 #: preferences/templates/preferences/display_preferences.html:66 msgid "Time before expiration of the reset password link (in hours)" msgstr "" "Temps avant expiration du lien de réinitialisation de mot de passe (en " "heures)" -#: preferences/forms.py:152 +#: preferences/forms.py:161 #: preferences/templates/preferences/display_preferences.html:52 msgid "Website name" msgstr "Nom du site" -#: preferences/forms.py:153 +#: preferences/forms.py:162 #: preferences/templates/preferences/display_preferences.html:54 msgid "Email address for automatic emailing" msgstr "Adresse mail pour les mails automatiques" -#: preferences/forms.py:154 +#: preferences/forms.py:163 #: preferences/templates/preferences/display_preferences.html:76 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: preferences/forms.py:155 +#: preferences/forms.py:164 #: preferences/templates/preferences/display_preferences.html:78 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: preferences/forms.py:168 +#: preferences/forms.py:177 msgid "Organisation name" msgstr "Nom de l'association" -#: preferences/forms.py:169 -#: preferences/templates/preferences/display_preferences.html:327 +#: preferences/forms.py:178 +#: preferences/templates/preferences/display_preferences.html:329 msgid "SIRET number" msgstr "Numéro SIRET" -#: preferences/forms.py:170 +#: preferences/forms.py:179 msgid "Address (line 1)" msgstr "Adresse (ligne 1)" -#: preferences/forms.py:171 +#: preferences/forms.py:180 msgid "Address (line 2)" msgstr "Adresse (ligne 2)" -#: preferences/forms.py:172 -#: preferences/templates/preferences/display_preferences.html:335 +#: preferences/forms.py:181 +#: preferences/templates/preferences/display_preferences.html:337 msgid "Contact email address" msgstr "Adresse mail de contact" -#: preferences/forms.py:173 -#: preferences/templates/preferences/display_preferences.html:339 +#: preferences/forms.py:182 +#: preferences/templates/preferences/display_preferences.html:341 msgid "Telephone number" msgstr "Numéro de téléphone" -#: preferences/forms.py:174 -#: preferences/templates/preferences/display_preferences.html:341 +#: preferences/forms.py:183 +#: preferences/templates/preferences/display_preferences.html:343 msgid "Usual name" msgstr "Nom d'usage" -#: preferences/forms.py:176 +#: preferences/forms.py:185 msgid "Account used for editing from /admin" msgstr "Compte utilisé pour les modifications depuis /admin" -#: preferences/forms.py:178 preferences/forms.py:323 +#: preferences/forms.py:187 preferences/forms.py:332 #: preferences/templates/preferences/aff_service.html:33 msgid "Description" msgstr "Description" -#: preferences/forms.py:192 +#: preferences/forms.py:201 msgid "Message for the French welcome email" msgstr "Message pour le mail de bienvenue en français" -#: preferences/forms.py:195 +#: preferences/forms.py:204 msgid "Message for the English welcome email" msgstr "Message pour le mail de bienvenue en anglais" -#: preferences/forms.py:209 +#: preferences/forms.py:218 msgid "Facebook URL" msgstr "URL du compte Facebook" -#: preferences/forms.py:210 +#: preferences/forms.py:219 msgid "Twitter URL" msgstr "URL du compte Twitter" -#: preferences/forms.py:211 -#: preferences/templates/preferences/display_preferences.html:502 +#: preferences/forms.py:220 +#: preferences/templates/preferences/display_preferences.html:504 msgid "Twitter account name" msgstr "Nom du compte Twitter" -#: preferences/forms.py:229 +#: preferences/forms.py:238 msgid "You chose to set vlan but did not set any VLAN." msgstr "" "Vous avez choisi de paramétrer vlan mais vous n'avez indiqué aucun VLAN." -#: preferences/forms.py:230 +#: preferences/forms.py:239 msgid "Please, choose a VLAN." msgstr "Veuillez choisir un VLAN." -#: preferences/forms.py:261 +#: preferences/forms.py:270 msgid "There is already a mandate taking place at the specified start date." msgstr "Il y a déjà un mandat ayant cours à la date de début renseignée." -#: preferences/forms.py:275 +#: preferences/forms.py:284 msgid "There is already a mandate taking place at the specified end date." msgstr "Il y a déjà un madant ayant cours à la date de fin renseignée." -#: preferences/forms.py:289 +#: preferences/forms.py:298 msgid "The specified dates overlap with an existing mandate." msgstr "Les dates renseignées se superposent avec un mandat existant." -#: preferences/forms.py:321 +#: preferences/forms.py:330 #: preferences/templates/preferences/aff_service.html:31 -#: preferences/templates/preferences/display_preferences.html:325 +#: preferences/templates/preferences/display_preferences.html:327 msgid "Name" msgstr "Nom" -#: preferences/forms.py:322 +#: preferences/forms.py:331 #: preferences/templates/preferences/aff_service.html:32 msgid "URL" msgstr "URL" -#: preferences/forms.py:324 +#: preferences/forms.py:333 #: preferences/templates/preferences/aff_service.html:34 msgid "Image" msgstr "Image" -#: preferences/forms.py:332 +#: preferences/forms.py:341 msgid "Current services" msgstr "Services actuels" -#: preferences/forms.py:421 +#: preferences/forms.py:430 msgid "Current email addresses" msgstr "Adresses mail actuelles" -#: preferences/forms.py:456 +#: preferences/forms.py:465 msgid "Current document templates" msgstr "Modèles de document actuels" -#: preferences/forms.py:486 +#: preferences/forms.py:495 msgid "Current attributes" msgstr "Attributs actuels" -#: preferences/models.py:56 +#: preferences/models.py:78 msgid "Users can't select their room" msgstr "Les utilisateurs ne peuvent pas modifier leur chambre" -#: preferences/models.py:57 +#: preferences/models.py:82 msgid "" "Users can only select a room occupied by a user with a disabled connection." msgstr "" "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la " "connexion est désactivée." -#: preferences/models.py:58 +#: preferences/models.py:85 msgid "Users can select all rooms" msgstr "Les utilisateurs peuvent choisir toutes les chambres" -#: preferences/models.py:64 +#: preferences/models.py:91 msgid "Users can create a club." msgstr "Les utilisateurs peuvent créer un club." -#: preferences/models.py:67 +#: preferences/models.py:94 msgid "Users can create a member." msgstr "Les utilisateurs peuvent créer un adhérent." -#: preferences/models.py:73 +#: preferences/models.py:100 msgid "Users can edit their shell." msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande." -#: preferences/models.py:79 +#: preferences/models.py:103 +msgid "Users can edit their pseudo." +msgstr "Les utilisateurs peuvent modifier leur pseudo" + +#: preferences/models.py:109 msgid "Policy on self users room edition" msgstr "Autorisation d'édtion du champ chambre par les utilisateurs" -#: preferences/models.py:82 +#: preferences/models.py:112 msgid "Enable local email accounts for users." msgstr "Activer les comptes mail locaux pour les utilisateurs." -#: preferences/models.py:87 +#: preferences/models.py:117 msgid "Domain to use for local email accounts." msgstr "Domaine à utiliser pour les comptes mail locaux." -#: preferences/models.py:91 +#: preferences/models.py:121 msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:96 +#: preferences/models.py:125 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:102 +#: preferences/models.py:130 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -314,11 +356,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:106 +#: preferences/models.py:134 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:111 +#: preferences/models.py:139 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -326,7 +368,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:118 +#: preferences/models.py:146 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -337,172 +379,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:125 +#: preferences/models.py:153 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:129 +#: preferences/models.py:157 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:130 +#: preferences/models.py:158 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:137 +#: preferences/models.py:165 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:155 +#: preferences/models.py:183 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:156 +#: preferences/models.py:184 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:157 +#: preferences/models.py:185 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:166 +#: preferences/models.py:194 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:176 +#: preferences/models.py:204 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:177 +#: preferences/models.py:205 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:197 preferences/models.py:655 +#: preferences/models.py:225 preferences/models.py:687 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:198 preferences/models.py:656 +#: preferences/models.py:226 preferences/models.py:688 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:204 +#: preferences/models.py:232 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:209 +#: preferences/models.py:237 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:215 +#: preferences/models.py:243 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:222 +#: preferences/models.py:250 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:228 +#: preferences/models.py:256 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:231 +#: preferences/models.py:259 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:234 +#: preferences/models.py:262 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:338 +#: preferences/models.py:367 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:339 +#: preferences/models.py:369 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:352 +#: preferences/models.py:382 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:354 +#: preferences/models.py:384 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:357 +#: preferences/models.py:387 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:361 +#: preferences/models.py:391 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:362 preferences/views.py:307 +#: preferences/models.py:392 preferences/views.py:335 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:363 -#: preferences/templates/preferences/display_preferences.html:221 +#: preferences/models.py:393 +#: preferences/templates/preferences/display_preferences.html:223 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:370 +#: preferences/models.py:400 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:373 +#: preferences/models.py:403 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:379 +#: preferences/models.py:409 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:380 +#: preferences/models.py:410 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:382 +#: preferences/models.py:412 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:389 +#: preferences/models.py:419 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:392 preferences/views.py:370 +#: preferences/models.py:422 preferences/views.py:397 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:395 +#: preferences/models.py:425 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:407 +#: preferences/models.py:437 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:413 +#: preferences/models.py:443 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:417 +#: preferences/models.py:447 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:418 preferences/views.py:252 +#: preferences/models.py:448 preferences/views.py:280 msgid "reminder" msgstr "rappel" -#: preferences/models.py:419 +#: preferences/models.py:449 msgid "reminders" msgstr "rappels" -#: preferences/models.py:440 +#: preferences/models.py:470 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -510,7 +552,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:448 +#: preferences/models.py:478 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -518,75 +560,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:463 +#: preferences/models.py:493 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:464 +#: preferences/models.py:494 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:484 +#: preferences/models.py:514 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:485 preferences/views.py:203 +#: preferences/models.py:515 preferences/views.py:231 msgid "service" msgstr "service" -#: preferences/models.py:486 +#: preferences/models.py:516 msgid "services" msgstr "services" -#: preferences/models.py:496 +#: preferences/models.py:526 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:502 +#: preferences/models.py:532 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:512 +#: preferences/models.py:542 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:514 +#: preferences/models.py:544 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:515 +#: preferences/models.py:545 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:523 preferences/views.py:610 +#: preferences/models.py:553 preferences/views.py:635 msgid "mandate" msgstr "mandat" -#: preferences/models.py:524 +#: preferences/models.py:554 msgid "mandates" msgstr "mandats" -#: preferences/models.py:525 +#: preferences/models.py:555 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:532 +#: preferences/models.py:562 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:533 +#: preferences/models.py:563 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:535 +#: preferences/models.py:565 msgid "start date" msgstr "date de début" -#: preferences/models.py:536 +#: preferences/models.py:566 msgid "end date" msgstr "date de fin" -#: preferences/models.py:549 +#: preferences/models.py:580 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -594,140 +636,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:564 +#: preferences/models.py:596 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:567 +#: preferences/models.py:599 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:568 +#: preferences/models.py:600 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:571 +#: preferences/models.py:603 msgid "Organisation" msgstr "Association" -#: preferences/models.py:578 +#: preferences/models.py:610 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:579 +#: preferences/models.py:611 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:597 +#: preferences/models.py:629 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:598 +#: preferences/models.py:630 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:612 +#: preferences/models.py:644 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:615 +#: preferences/models.py:647 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:620 +#: preferences/models.py:652 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:622 +#: preferences/models.py:654 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:627 +#: preferences/models.py:659 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:628 +#: preferences/models.py:660 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:632 preferences/views.py:563 +#: preferences/models.py:664 preferences/views.py:588 msgid "attribute" msgstr "attribut" -#: preferences/models.py:633 +#: preferences/models.py:665 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:635 +#: preferences/models.py:667 msgid "value" msgstr "valeur" -#: preferences/models.py:637 +#: preferences/models.py:669 msgid "comment" msgstr "commentaire" -#: preferences/models.py:638 +#: preferences/models.py:670 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:649 +#: preferences/models.py:681 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:650 -#: preferences/templates/preferences/display_preferences.html:299 +#: preferences/models.py:682 +#: preferences/templates/preferences/display_preferences.html:301 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:661 +#: preferences/models.py:693 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:662 +#: preferences/models.py:694 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:671 +#: preferences/models.py:703 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:679 +#: preferences/models.py:711 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:680 +#: preferences/models.py:712 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:686 +#: preferences/models.py:718 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:687 +#: preferences/models.py:719 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:693 +#: preferences/models.py:725 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:701 +#: preferences/models.py:733 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:702 +#: preferences/models.py:734 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:708 +#: preferences/models.py:740 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:709 +#: preferences/models.py:741 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:716 +#: preferences/models.py:748 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -735,87 +777,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:726 +#: preferences/models.py:758 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:727 +#: preferences/models.py:759 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:733 +#: preferences/models.py:765 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:734 +#: preferences/models.py:766 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:740 +#: preferences/models.py:772 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:748 +#: preferences/models.py:780 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:749 +#: preferences/models.py:781 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:755 +#: preferences/models.py:787 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:756 +#: preferences/models.py:788 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:762 +#: preferences/models.py:794 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:770 +#: preferences/models.py:802 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:771 +#: preferences/models.py:803 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:777 +#: preferences/models.py:809 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:778 +#: preferences/models.py:810 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:791 +#: preferences/models.py:823 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:792 +#: preferences/models.py:824 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:819 +#: preferences/models.py:851 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:823 +#: preferences/models.py:855 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:830 +#: preferences/models.py:862 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:836 +#: preferences/models.py:868 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:838 +#: preferences/models.py:870 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -823,19 +865,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:850 +#: preferences/models.py:882 msgid "template" msgstr "modèle" -#: preferences/models.py:851 +#: preferences/models.py:883 msgid "name" msgstr "nom" -#: preferences/models.py:854 +#: preferences/models.py:886 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:855 +#: preferences/models.py:887 msgid "document templates" msgstr "modèles de document" @@ -848,7 +890,7 @@ msgid "File" msgstr "Fichier" #: preferences/templates/preferences/aff_mailcontact.html:31 -#: preferences/templates/preferences/display_preferences.html:331 +#: preferences/templates/preferences/display_preferences.html:333 msgid "Address" msgstr "Adresse" @@ -1028,16 +1070,16 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:46 #: preferences/templates/preferences/display_preferences.html:108 -#: preferences/templates/preferences/display_preferences.html:187 -#: preferences/templates/preferences/display_preferences.html:240 -#: preferences/templates/preferences/display_preferences.html:302 -#: preferences/templates/preferences/display_preferences.html:320 -#: preferences/templates/preferences/display_preferences.html:417 -#: preferences/templates/preferences/display_preferences.html:495 +#: preferences/templates/preferences/display_preferences.html:189 +#: preferences/templates/preferences/display_preferences.html:242 +#: preferences/templates/preferences/display_preferences.html:304 +#: preferences/templates/preferences/display_preferences.html:322 +#: preferences/templates/preferences/display_preferences.html:419 +#: preferences/templates/preferences/display_preferences.html:497 #: preferences/templates/preferences/edit_preferences.html:46 -#: preferences/views.py:188 preferences/views.py:237 preferences/views.py:283 -#: preferences/views.py:343 preferences/views.py:407 preferences/views.py:472 -#: preferences/views.py:548 preferences/views.py:595 +#: preferences/views.py:216 preferences/views.py:265 preferences/views.py:311 +#: preferences/views.py:368 preferences/views.py:432 preferences/views.py:497 +#: preferences/views.py:573 preferences/views.py:620 msgid "Edit" msgstr "Modifier" @@ -1057,15 +1099,6 @@ msgstr "Message général affiché sur le site" msgid "Main site URL" msgstr "URL du site principal" -#: preferences/templates/preferences/display_preferences.html:84 -msgid "Local email accounts enabled" -msgstr "Comptes mail locaux activés" - -#: preferences/templates/preferences/display_preferences.html:86 -#: preferences/templates/preferences/display_preferences.html:164 -msgid "Local email domain" -msgstr "Domaine de mail local" - #: preferences/templates/preferences/display_preferences.html:90 msgid "Maximum number of email aliases allowed" msgstr "Nombre maximum d'alias mail autorisé pour un utilisateur standard" @@ -1076,7 +1109,7 @@ msgstr "Préférences d'utilisateur" #: preferences/templates/preferences/display_preferences.html:112 msgid "Accounts creation and self-register" -msgstr "" +msgstr "Creation des comptes et autoenregistrement" #: preferences/templates/preferences/display_preferences.html:115 msgid "Creation of members by everyone" @@ -1109,6 +1142,10 @@ msgstr "" "Permettre le choix d'un mot de passe directement lors de la création du " "compte" +#: preferences/templates/preferences/display_preferences.html:135 +msgid "Delay before disabling accounts without a verified email" +msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" + #: preferences/templates/preferences/display_preferences.html:136 #, python-format msgid "%(disable_emailnotyetconfirmed)s days" @@ -1134,224 +1171,228 @@ msgstr "Champ empreinte GPG" msgid "Policy for self-user room change" msgstr "Autorisations pour le changement de chambre des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:159 +#: preferences/templates/preferences/display_preferences.html:157 +msgid "Users can edit their pseudo" +msgstr "Les utilisateurs peuvent modifier leur pseudo" + +#: preferences/templates/preferences/display_preferences.html:161 msgid "Local email accounts settings" msgstr "Réglages des comptes email locaux" -#: preferences/templates/preferences/display_preferences.html:162 +#: preferences/templates/preferences/display_preferences.html:164 msgid "Local email accounts state" msgstr "Etat des comptes email locaux" -#: preferences/templates/preferences/display_preferences.html:168 +#: preferences/templates/preferences/display_preferences.html:170 msgid "Maximum of local email address" msgstr "Maximum de mail local autorisés" -#: preferences/templates/preferences/display_preferences.html:179 +#: preferences/templates/preferences/display_preferences.html:181 msgid "Machine preferences" msgstr "Préférences de machine" -#: preferences/templates/preferences/display_preferences.html:192 +#: preferences/templates/preferences/display_preferences.html:194 msgid "Password per machine" msgstr "Mot de passe par machine" -#: preferences/templates/preferences/display_preferences.html:200 +#: preferences/templates/preferences/display_preferences.html:202 msgid "Default Time To Live (TTL) for CNAME, A and AAAA records." msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA." -#: preferences/templates/preferences/display_preferences.html:204 +#: preferences/templates/preferences/display_preferences.html:206 msgid "IPv6 support" msgstr "Support de l'IPv6" -#: preferences/templates/preferences/display_preferences.html:206 +#: preferences/templates/preferences/display_preferences.html:208 msgid "Creation of machines" msgstr "Création de machines" -#: preferences/templates/preferences/display_preferences.html:216 +#: preferences/templates/preferences/display_preferences.html:218 msgid "Topology preferences" msgstr "Préférences de topologie" -#: preferences/templates/preferences/display_preferences.html:223 +#: preferences/templates/preferences/display_preferences.html:225 msgid "Add a RADIUS key" msgstr "Ajouter une clé RADIUS" -#: preferences/templates/preferences/display_preferences.html:233 +#: preferences/templates/preferences/display_preferences.html:235 msgid "Configuration of switches" msgstr "Configuration de commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:246 +#: preferences/templates/preferences/display_preferences.html:248 msgid "Web management, activated in case of automatic provision" msgstr "Gestion web, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:248 +#: preferences/templates/preferences/display_preferences.html:250 msgid "REST management, activated in case of automatic provision" msgstr "Gestion REST, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:255 +#: preferences/templates/preferences/display_preferences.html:257 msgid "Provision of configuration for switches" msgstr "Provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:258 +#: preferences/templates/preferences/display_preferences.html:260 msgid "Switches with automatic provision" msgstr "Commutateurs réseau avec provision automatique" -#: preferences/templates/preferences/display_preferences.html:259 -#: preferences/templates/preferences/display_preferences.html:263 -#: preferences/templates/preferences/display_preferences.html:267 -#: preferences/templates/preferences/display_preferences.html:275 -#: preferences/templates/preferences/display_preferences.html:279 -#: preferences/templates/preferences/display_preferences.html:289 +#: preferences/templates/preferences/display_preferences.html:261 +#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:277 +#: preferences/templates/preferences/display_preferences.html:281 +#: preferences/templates/preferences/display_preferences.html:291 msgid "OK" msgstr "OK" -#: preferences/templates/preferences/display_preferences.html:259 -#: preferences/templates/preferences/display_preferences.html:263 -#: preferences/templates/preferences/display_preferences.html:267 -#: preferences/templates/preferences/display_preferences.html:289 +#: preferences/templates/preferences/display_preferences.html:261 +#: preferences/templates/preferences/display_preferences.html:265 +#: preferences/templates/preferences/display_preferences.html:269 +#: preferences/templates/preferences/display_preferences.html:291 msgid "Missing" msgstr "Manquant" -#: preferences/templates/preferences/display_preferences.html:262 +#: preferences/templates/preferences/display_preferences.html:264 msgid "IP range for the management of switches" msgstr "Plage d'IP pour la gestion des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:266 +#: preferences/templates/preferences/display_preferences.html:268 msgid "Server for the configuration of switches" msgstr "Serveur pour la configuration des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:270 +#: preferences/templates/preferences/display_preferences.html:272 msgid "Provision of configuration mode for switches" msgstr "Mode de provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:274 +#: preferences/templates/preferences/display_preferences.html:276 msgid "TFTP mode" msgstr "Mode TFTP" -#: preferences/templates/preferences/display_preferences.html:278 +#: preferences/templates/preferences/display_preferences.html:280 msgid "SFTP mode" msgstr "Mode SFTP" -#: preferences/templates/preferences/display_preferences.html:279 +#: preferences/templates/preferences/display_preferences.html:281 msgid "Missing credentials" msgstr "Identifiants manquants" -#: preferences/templates/preferences/display_preferences.html:283 +#: preferences/templates/preferences/display_preferences.html:285 msgid "Switch management credentials" msgstr "Identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:285 +#: preferences/templates/preferences/display_preferences.html:287 msgid "Add switch management credentials" msgstr "Ajouter des identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:296 +#: preferences/templates/preferences/display_preferences.html:298 msgid "RADIUS preferences" msgstr "Préférences RADIUS" -#: preferences/templates/preferences/display_preferences.html:305 +#: preferences/templates/preferences/display_preferences.html:307 msgid "Current RADIUS attributes" msgstr "Attributs RADIUS actuels" -#: preferences/templates/preferences/display_preferences.html:306 +#: preferences/templates/preferences/display_preferences.html:308 msgid "Add an attribute" msgstr "Ajouter un attribut" -#: preferences/templates/preferences/display_preferences.html:314 +#: preferences/templates/preferences/display_preferences.html:316 msgid "Information about the organisation" msgstr "Informations sur l'association" -#: preferences/templates/preferences/display_preferences.html:345 +#: preferences/templates/preferences/display_preferences.html:347 msgid "User object of the organisation" msgstr "Objet utilisateur de l'association" -#: preferences/templates/preferences/display_preferences.html:347 +#: preferences/templates/preferences/display_preferences.html:349 msgid "Description of the organisation" msgstr "Description de l'association" -#: preferences/templates/preferences/display_preferences.html:351 +#: preferences/templates/preferences/display_preferences.html:353 msgid "Mandates" msgstr "Mandats" -#: preferences/templates/preferences/display_preferences.html:354 +#: preferences/templates/preferences/display_preferences.html:356 msgid "Add a mandate" msgstr "Ajouter un mandat" -#: preferences/templates/preferences/display_preferences.html:363 +#: preferences/templates/preferences/display_preferences.html:365 msgid "Document templates" msgstr "Modèles de document" -#: preferences/templates/preferences/display_preferences.html:369 +#: preferences/templates/preferences/display_preferences.html:371 msgid "Add a document template" msgstr "Ajouter un modèle de document" -#: preferences/templates/preferences/display_preferences.html:373 +#: preferences/templates/preferences/display_preferences.html:375 msgid "Delete one or several document templates" msgstr " Supprimer un ou plusieurs modèles de document" -#: preferences/templates/preferences/display_preferences.html:382 +#: preferences/templates/preferences/display_preferences.html:384 msgid "Subscription preferences" msgstr "Préférences de cotisation" -#: preferences/templates/preferences/display_preferences.html:391 +#: preferences/templates/preferences/display_preferences.html:393 msgid "Send voucher by email" msgstr "Envoyer le reçu par mail" -#: preferences/templates/preferences/display_preferences.html:395 +#: preferences/templates/preferences/display_preferences.html:397 msgid "Invoices' template" msgstr "Modèle des factures" -#: preferences/templates/preferences/display_preferences.html:399 +#: preferences/templates/preferences/display_preferences.html:401 msgid "Vouchers' template" msgstr "Modèle des reçus" -#: preferences/templates/preferences/display_preferences.html:410 +#: preferences/templates/preferences/display_preferences.html:412 msgid "Message for emails" msgstr "Message pour les mails" -#: preferences/templates/preferences/display_preferences.html:423 +#: preferences/templates/preferences/display_preferences.html:425 msgid "Welcome email (in French)" msgstr "Mail de bienvenue (en français)" -#: preferences/templates/preferences/display_preferences.html:427 +#: preferences/templates/preferences/display_preferences.html:429 msgid "Welcome email (in English)" msgstr "Mail de bienvenue (en anglais)" -#: preferences/templates/preferences/display_preferences.html:437 +#: preferences/templates/preferences/display_preferences.html:439 msgid "Preferences for the membership's end email" msgstr "Préférences pour le mail de fin d'adhésion" -#: preferences/templates/preferences/display_preferences.html:443 +#: preferences/templates/preferences/display_preferences.html:445 msgid "Add a reminder" msgstr "Ajouter un rappel" -#: preferences/templates/preferences/display_preferences.html:454 +#: preferences/templates/preferences/display_preferences.html:456 msgid "List of services and homepage preferences" msgstr "Liste des services et préférences de page d'accueil" -#: preferences/templates/preferences/display_preferences.html:460 +#: preferences/templates/preferences/display_preferences.html:462 msgid "Add a service" msgstr "Ajouter un service" -#: preferences/templates/preferences/display_preferences.html:471 +#: preferences/templates/preferences/display_preferences.html:473 msgid "List of contact email addresses" msgstr "Liste des adresses mail de contact" -#: preferences/templates/preferences/display_preferences.html:477 +#: preferences/templates/preferences/display_preferences.html:479 msgid "Add an address" msgstr "Ajouter une adresse" -#: preferences/templates/preferences/display_preferences.html:479 +#: preferences/templates/preferences/display_preferences.html:481 msgid "Delete one or several addresses" msgstr "Supprimer une ou plusieurs adresses" -#: preferences/templates/preferences/display_preferences.html:488 +#: preferences/templates/preferences/display_preferences.html:490 msgid "Social networks" msgstr "Réseaux sociaux" -#: preferences/templates/preferences/display_preferences.html:500 +#: preferences/templates/preferences/display_preferences.html:502 msgid "Twitter account URL" msgstr "URL du compte Twitter" -#: preferences/templates/preferences/display_preferences.html:506 +#: preferences/templates/preferences/display_preferences.html:508 msgid "Facebook account URL" msgstr "URL du compte Facebook" @@ -1359,75 +1400,75 @@ msgstr "URL du compte Facebook" msgid "Editing of preferences" msgstr "Modification des préférences" -#: preferences/utils/views.py:45 +#: preferences/views.py:95 msgid "Unknown object." msgstr "Objet inconnu." -#: preferences/utils/views.py:64 +#: preferences/views.py:114 msgid "The preferences were edited." msgstr "Les préférences ont été modifiées." -#: preferences/views.py:167 +#: preferences/views.py:195 msgid "The service was added." msgstr "Le service a été ajouté." -#: preferences/views.py:170 preferences/views.py:219 preferences/views.py:268 -#: preferences/views.py:325 preferences/views.py:388 preferences/views.py:447 -#: preferences/views.py:530 preferences/views.py:579 +#: preferences/views.py:198 preferences/views.py:247 preferences/views.py:296 +#: preferences/views.py:351 preferences/views.py:414 preferences/views.py:472 +#: preferences/views.py:555 preferences/views.py:604 msgid "Add" msgstr "Ajouter" -#: preferences/views.py:185 +#: preferences/views.py:213 msgid "The service was edited." msgstr "Le service a été modifié." -#: preferences/views.py:200 +#: preferences/views.py:228 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: preferences/views.py:216 +#: preferences/views.py:244 msgid "The reminder was added." msgstr "Le rappel a été ajouté." -#: preferences/views.py:234 +#: preferences/views.py:262 msgid "The reminder was edited." msgstr "Le rappel a été modifié." -#: preferences/views.py:249 +#: preferences/views.py:277 msgid "The reminder was deleted." msgstr "Le rappel a été supprimé." -#: preferences/views.py:265 +#: preferences/views.py:293 msgid "The RADIUS key was added." msgstr "La clé RADIUS a été ajoutée." -#: preferences/views.py:280 +#: preferences/views.py:308 msgid "The RADIUS key was edited." msgstr "La clé RADIUS a été modifiée." -#: preferences/views.py:296 +#: preferences/views.py:324 msgid "The RADIUS key was deleted." msgstr "La clé RADIUS a été supprimée." -#: preferences/views.py:301 +#: preferences/views.py:329 msgid "The RADIUS key is assigned to at least one switch, you can't delete it." msgstr "" "La clé RADIUS est assignée a au moins un commutateur réseau, vous ne pouvez " "pas la supprimer." -#: preferences/views.py:320 +#: preferences/views.py:348 msgid "The switch management credentials were added." msgstr "Les identifiants de gestion de commutateur réseay ont été ajoutés." -#: preferences/views.py:340 +#: preferences/views.py:365 msgid "The switch management credentials were edited." msgstr "Les identifiants de gestion de commutateur réseau ont été modifiés." -#: preferences/views.py:357 +#: preferences/views.py:382 msgid "The switch management credentials were deleted." msgstr "Les identifiants de gestion de commutateur réseau ont été supprimés." -#: preferences/views.py:363 +#: preferences/views.py:388 msgid "" "The switch management credentials are assigned to at least one switch, you " "can't delete them." @@ -1435,44 +1476,44 @@ msgstr "" "Les identifiants de gestion de commutateur réseau sont assignés à au moins " "un commutateur réseau , vous ne pouvez pas les supprimer." -#: preferences/views.py:383 +#: preferences/views.py:411 msgid "The contact email address was created." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:404 +#: preferences/views.py:429 msgid "The contact email address was edited." msgstr "L'adresse mail de contact a été modifiée." -#: preferences/views.py:422 +#: preferences/views.py:447 msgid "The contact email adress was deleted." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:425 preferences/views.py:512 +#: preferences/views.py:450 preferences/views.py:537 msgid "Delete" msgstr "Supprimer" -#: preferences/views.py:442 +#: preferences/views.py:467 msgid "The document template was created." msgstr "Le modèle de document a été créé." -#: preferences/views.py:448 +#: preferences/views.py:473 msgid "New document template" msgstr "Nouveau modèle de document" -#: preferences/views.py:467 +#: preferences/views.py:492 msgid "The document template was edited." msgstr "Le modèle de document a été édité." -#: preferences/views.py:473 +#: preferences/views.py:498 msgid "Edit document template" msgstr "Modifier le modèle de document" -#: preferences/views.py:496 +#: preferences/views.py:521 #, python-format msgid "The document template %(document_template)s was deleted." msgstr "Le modèle de document %(document_template)s a été supprimé." -#: preferences/views.py:503 +#: preferences/views.py:528 #, python-format msgid "" "The document template %(document_template)s can't be deleted because it is " @@ -1481,31 +1522,31 @@ msgstr "" "Le modèle de document %(document_template)s ne peut pas être supprimé car il " "est actuellement utilisé." -#: preferences/views.py:513 +#: preferences/views.py:538 msgid "Delete document template" msgstr "Supprimer le modèle de document" -#: preferences/views.py:527 +#: preferences/views.py:552 msgid "The attribute was added." msgstr "L'attribut a été ajouté." -#: preferences/views.py:545 +#: preferences/views.py:570 msgid "The attribute was edited." msgstr "L'attribut a été modifié." -#: preferences/views.py:560 +#: preferences/views.py:585 msgid "The attribute was deleted." msgstr "L'attribut a été supprimé." -#: preferences/views.py:576 +#: preferences/views.py:601 msgid "The mandate was added." msgstr "Le mandat a été ajouté." -#: preferences/views.py:592 +#: preferences/views.py:617 msgid "The mandate was edited." msgstr "Le mandat a été modifié." -#: preferences/views.py:607 +#: preferences/views.py:632 msgid "The mandate was deleted." msgstr "Le mandat été supprimé." diff --git a/preferences/migrations/0071_optionaluser_self_change_pseudo.py b/preferences/migrations/0071_optionaluser_self_change_pseudo.py new file mode 100644 index 00000000..58214f3c --- /dev/null +++ b/preferences/migrations/0071_optionaluser_self_change_pseudo.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 09:01 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('preferences', '0070_auto_20200419_0225'), + ] + + operations = [ + migrations.AddField( + model_name='optionaluser', + name='self_change_pseudo', + field=models.BooleanField(default=True, help_text='Users can edit their pseudo.'), + ), + ] diff --git a/preferences/models.py b/preferences/models.py index 58badc18..d94b2ec8 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -99,6 +99,9 @@ class OptionalUser(AclMixin, PreferencesModel): self_change_shell = models.BooleanField( default=False, help_text=_("Users can edit their shell.") ) + self_change_pseudo = models.BooleanField( + default=True, help_text=_("Users can edit their pseudo.") + ) self_room_policy = models.CharField( max_length=32, choices=ROOM_POLICY, diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index 463a4d90..ede55c25 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -154,6 +154,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Policy for self-user room change" %} {{ useroptions.self_room_policy }} + {% trans "Users can edit their pseudo" %} + {{ useroptions.self_change_pseudo|tick }}

{% trans "Local email accounts settings" %}

diff --git a/users/models.py b/users/models.py index 4d9e1f20..4a3f7fb4 100755 --- a/users/models.py +++ b/users/models.py @@ -1168,6 +1168,28 @@ class User( else: return True, None, None + def can_change_pseudo(self, user_request, *_args, **_kwargs): + """ Check if a user can change a pseudo + + :param user_request: The user who request + :returns: a message and a boolean which is True if the user has + the right to change a shell + """ + if not ( + ( + self.pk == user_request.pk + and OptionalUser.get_cached_value("self_change_pseudo") + ) + or user_request.has_perm("users.change_user_shell") + ): + return ( + False, + _("You don't have the right to change the shell."), + ("users.change_user_shell",), + ) + else: + return True, None, None + @staticmethod def can_change_local_email_redirect(user_request, *_args, **_kwargs): """ Check if a user can change local_email_redirect. @@ -1314,6 +1336,7 @@ class User( super(User, self).__init__(*args, **kwargs) self.field_permissions = { "shell": self.can_change_shell, + "pseudo": self.can_change_pseudo, "force": self.can_change_force, "selfpasswd": self.check_selfpasswd, "local_email_redirect": self.can_change_local_email_redirect, From 69c924014f3d4f4196fe7f4c28bec34da2938955 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 23 Apr 2020 12:59:38 +0200 Subject: [PATCH 181/490] Fix permission check --- users/migrations/0091_auto_20200423_1256.py | 19 +++++++++++++++++++ users/models.py | 7 ++++--- 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 users/migrations/0091_auto_20200423_1256.py diff --git a/users/migrations/0091_auto_20200423_1256.py b/users/migrations/0091_auto_20200423_1256.py new file mode 100644 index 00000000..b2ffca92 --- /dev/null +++ b/users/migrations/0091_auto_20200423_1256.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.28 on 2020-04-23 10:56 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0090_auto_20200421_1825'), + ] + + operations = [ + migrations.AlterModelOptions( + name='user', + options={'permissions': (('change_user_password', 'Can change the password of a user'), ('change_user_state', 'Can edit the state of a user'), ('change_user_force', 'Can force the move'), ('change_user_shell', 'Can edit the shell of a user'), ('change_user_pseudo', 'Can edit the pseudo of a user'), ('change_user_groups', 'Can edit the groups of rights of a user (critical permission)'), ('change_all_users', 'Can edit all users, including those with rights'), ('view_user', 'Can view a user object')), 'verbose_name': 'user (member or club)', 'verbose_name_plural': 'users (members or clubs)'}, + ), + ] diff --git a/users/models.py b/users/models.py index 4a3f7fb4..9912893e 100755 --- a/users/models.py +++ b/users/models.py @@ -252,6 +252,7 @@ class User( ("change_user_state", _("Can edit the state of a user")), ("change_user_force", _("Can force the move")), ("change_user_shell", _("Can edit the shell of a user")), + ("change_user_pseudo", _("Can edit the pseudo of a user")), ( "change_user_groups", _("Can edit the groups of rights of a user (critical permission)"), @@ -1180,12 +1181,12 @@ class User( self.pk == user_request.pk and OptionalUser.get_cached_value("self_change_pseudo") ) - or user_request.has_perm("users.change_user_shell") + or user_request.has_perm("users.change_user_pseudo") ): return ( False, - _("You don't have the right to change the shell."), - ("users.change_user_shell",), + _("You don't have the right to change the pseudo."), + ("users.change_user_pseudo",), ) else: return True, None, None From cbf688b640be193dde3d4a764fa3cf2794ff80a9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 13:18:16 +0200 Subject: [PATCH 182/490] Create specific view for user history --- logs/models.py | 120 ++++++++++++++++++++++++-- logs/templates/logs/user_history.html | 71 +++++++++++++++ logs/urls.py | 1 + logs/views.py | 22 ++++- 4 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 logs/templates/logs/user_history.html diff --git a/logs/models.py b/logs/models.py index 343a33cb..01927de8 100644 --- a/logs/models.py +++ b/logs/models.py @@ -29,7 +29,7 @@ from machines.models import Machine from users.models import User -class HistoryEvent: +class MachineHistoryEvent: def __init__(self, user, machine, interface, start=None, end=None): """ :param user: User, The user owning the maching at the time of the event @@ -80,7 +80,7 @@ class MachineHistory: """ :param search: ip or mac to lookup :param params: dict built by the search view - :return: list or None, a list of HistoryEvent + :return: list or None, a list of MachineHistoryEvent """ self.start = params.get("s", None) self.end = params.get("e", None) @@ -101,7 +101,7 @@ class MachineHistory: :param machine: Version, the machine version related to the interface :param interface: Version, the interface targeted by this event """ - evt = HistoryEvent(user, machine, interface) + evt = MachineHistoryEvent(user, machine, interface) evt.start_date = interface.revision.date_created # Try not to recreate events if it's unnecessary @@ -177,7 +177,7 @@ class MachineHistory: def __get_by_ip(self, ip): """ :param ip: str, The IP to lookup - :returns: list, a list of HistoryEvent + :returns: list, a list of MachineHistoryEvent """ interfaces = self.__get_interfaces_for_ip(ip) @@ -193,7 +193,7 @@ class MachineHistory: def __get_by_mac(self, mac): """ :param mac: str, The MAC address to lookup - :returns: list, a list of HistoryEvent + :returns: list, a list of MachineHistoryEvent """ interfaces = self.__get_interfaces_for_mac(mac) @@ -205,3 +205,113 @@ class MachineHistory: self.__add_revision(user, machine, interface) return self.events + + +class UserHistoryEvent: + def __init__(self, user, version, previous_version=None, edited_fields=None): + """ + :param user: User, The user who's history is being built + :param version: Version, the version of the user for this event + :param previous_version: Version, the version of the user before this event + :param edited_fields: list, The list of modified fields by this event + """ + self.user = user + self.version = version + self.previous_version = previous_version + self.edited_fields = edited_fields + self.date = version.revision.date_created + self.performed_by = version.revision.user + self.comment = version.revision.get_comment() or None + + def edits(self, hide=["password", "pwd_ntlm"]): + """ + Build a list of the changes performed during this event + :param hide: list, the list of fields for which not to show details + :return: str + """ + edits = [] + + for field in self.edited_fields: + if field in hide: + # Don't show sensitive information + edits.append((field, None, None)) + else: + edits.append(( + field, + self.previous_version.field_dict[field], + self.version.field_dict[field] + )) + + return edits + + def __repr__(self): + return "{} edited fields {} of {} ({})".format( + self.performed_by, + self.edited_fields or "nothing", + self.user, + self.comment or "No comment" + ) + + +class UserHistory: + def __init__(self): + self.events = [] + self.user = None + self.__last_version = None + + def get(self, user_id): + """ + :param user_id: id of the user to lookup + :return: list or None, a list of UserHistoryEvent, in reverse chronological order + """ + self.events = [] + try: + self.user = User.objects.get(id=user_id) + except User.DoesNotExist: + return None + + # Get all the versions for this user, with the oldest first + user_versions = filter( + lambda x: x.field_dict["id"] == user_id, + Version.objects.get_for_model(User).order_by("revision__date_created") + ) + + for version in user_versions: + self.__add_revision(self.user, version) + + return self.events[::-1] + + def __compute_diff(self, v1, v2, ignoring=["last_login", "comment", "pwd_ntlm", "email_change_date"]): + """ + Find the edited field between two versions + :param v1: Version + :param v2: Version + :param ignoring: List, a list of fields to ignore + :return: List of field names + """ + fields = [] + + for key in v1.field_dict.keys(): + if key not in ignoring and v1.field_dict[key] != v2.field_dict[key]: + fields.append(key) + + return fields + + def __add_revision(self, user, version): + """ + Add a new revision to the chronological order + :param user: User, The user displayed in this history + :param version: Version, The version of the user for this event + """ + diff = None + if self.__last_version is not None: + diff = self.__compute_diff(version, self.__last_version) + + # Ignore "empty" events like login + if not diff: + self.__last_version = version + return + + evt = UserHistoryEvent(user, version, self.__last_version, diff) + self.events.append(evt) + self.__last_version = version diff --git a/logs/templates/logs/user_history.html b/logs/templates/logs/user_history.html new file mode 100644 index 00000000..6cf86625 --- /dev/null +++ b/logs/templates/logs/user_history.html @@ -0,0 +1,71 @@ +{% extends 'logs/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 © 2020 Jean-Romain Garnier + +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 "History" %}{% endblock %} + +{% block content %} +

{% blocktrans %}History of {{ user }}{% endblocktrans %}

+ +{% if events %} + + + + + + + + + + {% for event in events %} + + + + + + + {% endfor %} +
{% trans "Performed by" %}{% trans "Date" %}{% trans "Diff" %}{% trans "Comment" %}
+ + {{ event.performed_by }} + + {{ event.date }} + {% for edit in event.edits %} + {% if edit[1] and edit[2] %} +

{{ edit[0] }}: {{ edit[1] }} ➔ {{ edit[2] }}

+ {% elif edit[2] %} +

{{ edit[0] }}: {{ edit[2] }}

+ {% else %} +

{{ edit[0] }}

+ {% endif %} + {% endfor %} +
{{ event.comment }}
+ {% include 'pagination.html' with list=events %} +{% else %} +

{% trans "No event" %}

+{% endif %} +
+
+
diff --git a/logs/urls.py b/logs/urls.py index eced2a83..adde5d3c 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -47,4 +47,5 @@ urlpatterns = [ name="history", ), url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), + url(r"^user/(?P[0-9]+)$", views.user_history, name="stats-user-history"), ] diff --git a/logs/views.py b/logs/views.py index 85ba35cf..a36e90cb 100644 --- a/logs/views.py +++ b/logs/views.py @@ -101,7 +101,7 @@ from re2o.utils import ( from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history -from .models import MachineHistory +from .models import MachineHistory, UserHistory from .forms import MachineHistoryForm @@ -508,6 +508,26 @@ def stats_search_machine_history(request): return render(request, "logs/search_machine_history.html", {"history_form": history_form}) +@login_required +@can_view_app("users") +def user_history(request, user_id): + history = UserHistory() + events = history.get(user_id) + + max_result = GeneralOption.get_cached_value("pagination_number") + events = re2o_paginator( + request, + events, + max_result + ) + + return render( + request, + "logs/user_history.html", + { "user": history.user, "events": events }, + ) + + def history(request, application, object_name, object_id): """Render history for a model. From d3e681a0ef28ca72bfd13cdbeb289d36cb9549bb Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 12:06:47 +0000 Subject: [PATCH 183/490] Fix displaying new user history view --- logs/models.py | 13 ++++-------- logs/templates/logs/user_history.html | 30 ++++++++++++++++----------- logs/urls.py | 2 +- logs/views.py | 10 ++++----- 4 files changed, 28 insertions(+), 27 deletions(-) diff --git a/logs/models.py b/logs/models.py index 01927de8..0e544215 100644 --- a/logs/models.py +++ b/logs/models.py @@ -256,28 +256,23 @@ class UserHistoryEvent: class UserHistory: def __init__(self): self.events = [] - self.user = None self.__last_version = None - def get(self, user_id): + def get(self, user): """ - :param user_id: id of the user to lookup + :param user: User, the user to lookup :return: list or None, a list of UserHistoryEvent, in reverse chronological order """ self.events = [] - try: - self.user = User.objects.get(id=user_id) - except User.DoesNotExist: - return None # Get all the versions for this user, with the oldest first user_versions = filter( - lambda x: x.field_dict["id"] == user_id, + lambda x: x.field_dict["id"] == user.id, Version.objects.get_for_model(User).order_by("revision__date_created") ) for version in user_versions: - self.__add_revision(self.user, version) + self.__add_revision(user, version) return self.events[::-1] diff --git a/logs/templates/logs/user_history.html b/logs/templates/logs/user_history.html index 6cf86625..b7714978 100644 --- a/logs/templates/logs/user_history.html +++ b/logs/templates/logs/user_history.html @@ -33,28 +33,32 @@ with this program; if not, write to the Free Software Foundation, Inc., - - + + {% for event in events %} - + @@ -69,3 +73,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,


+ +{% endblock %} diff --git a/logs/urls.py b/logs/urls.py index adde5d3c..562bd93c 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -47,5 +47,5 @@ urlpatterns = [ name="history", ), url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), - url(r"^user/(?P[0-9]+)$", views.user_history, name="stats-user-history"), + url(r"^user/(?P[0-9]+)$", views.user_history, name="user-history"), ] diff --git a/logs/views.py b/logs/views.py index a36e90cb..25ee7403 100644 --- a/logs/views.py +++ b/logs/views.py @@ -99,7 +99,7 @@ from re2o.utils import ( all_active_interfaces_count, ) from re2o.base import re2o_paginator, SortTable -from re2o.acl import can_view_all, can_view_app, can_edit_history +from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view from .models import MachineHistory, UserHistory from .forms import MachineHistoryForm @@ -509,10 +509,10 @@ def stats_search_machine_history(request): @login_required -@can_view_app("users") -def user_history(request, user_id): +@can_view(User) +def user_history(request, users, **_kwargs): history = UserHistory() - events = history.get(user_id) + events = history.get(users) max_result = GeneralOption.get_cached_value("pagination_number") events = re2o_paginator( @@ -524,7 +524,7 @@ def user_history(request, user_id): return render( request, "logs/user_history.html", - { "user": history.user, "events": events }, + { "user": users, "events": events }, ) From 7ed004f5644d65415905f45e1bd03b6ca3ae9b1c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 14:22:14 +0200 Subject: [PATCH 184/490] Improve event representation in user history --- logs/models.py | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/logs/models.py b/logs/models.py index 0e544215..c3254503 100644 --- a/logs/models.py +++ b/logs/models.py @@ -22,6 +22,8 @@ The models definitions for the logs app """ from reversion.models import Version +from django.utils.translation import ugettext_lazy as _ +from django.contrib.auth.models import Group from machines.models import IpList from machines.models import Interface @@ -223,6 +225,39 @@ class UserHistoryEvent: self.performed_by = version.revision.user self.comment = version.revision.get_comment() or None + def __repr(self, name, value): + """ + Returns the best representation of the given field + :param name: the name of the field + :param value: the value of the field + :return: object + """ + if name == "groups" and value is not None: + # value is a list of ints + groups = [] + for gid in value: + # Try to get the group name, if it's not deleted + try: + groups.append(Group.objects.get(id=gid)) + except Group.DoesNotExist: + # TODO: Find the group name in the versions? + groups.append(_("Deleted")) + elif name == "state": + if value is not None: + return User.STATES[value] + else: + return _("Unknown") + elif name == "email_state": + if value is not None: + return User.EMAIL_STATES[value] + else: + return _("Unknown") + + if value is None: + return _("None") + + return value + def edits(self, hide=["password", "pwd_ntlm"]): """ Build a list of the changes performed during this event @@ -238,8 +273,8 @@ class UserHistoryEvent: else: edits.append(( field, - self.previous_version.field_dict[field], - self.version.field_dict[field] + self.__repr(field, self.previous_version.field_dict[field]), + self.__repr(field, self.version.field_dict[field]) )) return edits @@ -276,7 +311,7 @@ class UserHistory: return self.events[::-1] - def __compute_diff(self, v1, v2, ignoring=["last_login", "comment", "pwd_ntlm", "email_change_date"]): + def __compute_diff(self, v1, v2, ignoring=["last_login", "pwd_ntlm", "email_change_date"]): """ Find the edited field between two versions :param v1: Version From 156a0692389acb151bad5b577b228e7f62a50a3c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 12:44:23 +0000 Subject: [PATCH 185/490] Improve user history view event format --- logs/models.py | 14 ++++++++++---- logs/templates/logs/user_history.html | 13 ++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/logs/models.py b/logs/models.py index c3254503..2eb5b4ca 100644 --- a/logs/models.py +++ b/logs/models.py @@ -232,24 +232,30 @@ class UserHistoryEvent: :param value: the value of the field :return: object """ - if name == "groups" and value is not None: + if name == "groups": + if len(value) == 0: + # Removed all the user's groups + return _("None") + # value is a list of ints groups = [] for gid in value: # Try to get the group name, if it's not deleted try: - groups.append(Group.objects.get(id=gid)) + groups.append(Group.objects.get(id=gid).name) except Group.DoesNotExist: # TODO: Find the group name in the versions? groups.append(_("Deleted")) + + return ", ".join(groups) elif name == "state": if value is not None: - return User.STATES[value] + return User.STATES[value][1] else: return _("Unknown") elif name == "email_state": if value is not None: - return User.EMAIL_STATES[value] + return User.EMAIL_STATES[value][1] else: return _("Unknown") diff --git a/logs/templates/logs/user_history.html b/logs/templates/logs/user_history.html index b7714978..ce492281 100644 --- a/logs/templates/logs/user_history.html +++ b/logs/templates/logs/user_history.html @@ -53,12 +53,15 @@ with this program; if not, write to the Free Software Foundation, Inc., From 21cbddd7b916b0ed64e8bfb9a75cfea1f5f4d8f8 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 12:54:30 +0000 Subject: [PATCH 186/490] Change link in user profile to new history view --- templates/buttons/history.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/templates/buttons/history.html b/templates/buttons/history.html index 730023e8..f8eed813 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -23,7 +23,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load i18n %} + +{% if name == "user" %} + + {% if text %}{% trans "History" %}{% endif %} + +{% else %} {% if text %}{% trans "History" %}{% endif %} +{% endif %} From 7ddbc02835a4900543f7a3228631cecaac4647c7 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 23 Apr 2020 17:01:07 +0200 Subject: [PATCH 187/490] Rebase dev --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 51 +++- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 254 ++++++++++---------- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 32 +-- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 2 +- 12 files changed, 194 insertions(+), 161 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 3c08e4e5..f5f9153f 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index aca2607b..ac3900ea 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index bb8dc944..2c5265fc 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -58,6 +58,20 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" +#: logs/models.py:238 logs/models.py:263 +msgid "None" +msgstr "Aucun(e)" + +#: logs/models.py:248 +msgid "Deleted" +msgstr "Supprimé(e)" + +#: logs/models.py:255 logs/models.py:260 +#: logs/templates/logs/machine_history.html:55 +#: logs/templates/logs/user_history.html:51 +msgid "Unknown" +msgstr "Inconnu(e)" + #: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" @@ -77,6 +91,7 @@ msgstr "Date de modification" #: logs/templates/logs/aff_stats_logs.html:42 #: logs/templates/logs/machine_history.html:39 +#: logs/templates/logs/user_history.html:39 msgid "Comment" msgstr "Commentaire" @@ -113,6 +128,7 @@ msgid "Rank" msgstr "Rang" #: logs/templates/logs/aff_summary.html:37 +#: logs/templates/logs/user_history.html:36 msgid "Date" msgstr "Date" @@ -196,15 +212,11 @@ msgstr "Résultats de la recherche" msgid "User" msgstr "Utilisateur" -#: logs/templates/logs/machine_history.html:55 -msgid "Unknown" -msgstr "Inconnu(e)" - #: logs/templates/logs/machine_history.html:62 msgid "Now" msgstr "Maintenant" -#: logs/templates/logs/machine_history.html:70 +#: logs/templates/logs/machine_history.html:71 msgid "No result" msgstr "Aucun résultat" @@ -253,6 +265,27 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" +#: logs/templates/logs/user_history.html:27 +msgid "History" +msgstr "Historique" + +#: logs/templates/logs/user_history.html:30 +#, python-format +msgid "History of %(user)s" +msgstr "Historique de %(user)s" + +#: logs/templates/logs/user_history.html:37 +msgid "Performed by" +msgstr "Effectué(e) par" + +#: logs/templates/logs/user_history.html:38 +msgid "Edited" +msgstr "Modifié" + +#: logs/templates/logs/user_history.html:74 +msgid "No event" +msgstr "Aucun évènement" + #: logs/views.py:178 msgid "Nonexistent revision." msgstr "Révision inexistante." @@ -377,14 +410,14 @@ msgstr "droits" msgid "actions" msgstr "actions" -#: logs/views.py:529 +#: logs/views.py:554 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:535 +#: logs/views.py:560 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:542 +#: logs/views.py:567 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index f1726d25..5013ea49 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 14c22e92..f4daa664 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index dc41163f..2f109979 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 11:55+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -342,13 +342,13 @@ msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:125 +#: preferences/models.py:122 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:130 +#: preferences/models.py:127 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -356,11 +356,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:134 +#: preferences/models.py:131 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:139 +#: preferences/models.py:136 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -368,7 +368,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:146 +#: preferences/models.py:143 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -379,172 +379,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:153 +#: preferences/models.py:150 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:157 +#: preferences/models.py:154 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:158 +#: preferences/models.py:155 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:165 +#: preferences/models.py:162 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:183 +#: preferences/models.py:180 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:184 +#: preferences/models.py:181 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:185 +#: preferences/models.py:182 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:194 +#: preferences/models.py:191 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:204 +#: preferences/models.py:201 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:205 +#: preferences/models.py:202 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:225 preferences/models.py:687 +#: preferences/models.py:222 preferences/models.py:684 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:226 preferences/models.py:688 +#: preferences/models.py:223 preferences/models.py:685 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:232 +#: preferences/models.py:229 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:237 +#: preferences/models.py:234 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:243 +#: preferences/models.py:240 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:250 +#: preferences/models.py:247 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:256 +#: preferences/models.py:253 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:259 +#: preferences/models.py:256 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:262 +#: preferences/models.py:259 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:367 +#: preferences/models.py:364 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:369 +#: preferences/models.py:366 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:382 +#: preferences/models.py:379 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:384 +#: preferences/models.py:381 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:387 +#: preferences/models.py:384 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:391 +#: preferences/models.py:388 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:392 preferences/views.py:335 +#: preferences/models.py:389 preferences/views.py:335 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:393 -#: preferences/templates/preferences/display_preferences.html:223 +#: preferences/models.py:390 +#: preferences/templates/preferences/display_preferences.html:221 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:400 +#: preferences/models.py:397 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:403 +#: preferences/models.py:400 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:409 +#: preferences/models.py:406 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:410 +#: preferences/models.py:407 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:412 +#: preferences/models.py:409 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:419 +#: preferences/models.py:416 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:422 preferences/views.py:397 +#: preferences/models.py:419 preferences/views.py:397 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:425 +#: preferences/models.py:422 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:437 +#: preferences/models.py:434 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:443 +#: preferences/models.py:440 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:447 +#: preferences/models.py:444 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:448 preferences/views.py:280 +#: preferences/models.py:445 preferences/views.py:280 msgid "reminder" msgstr "rappel" -#: preferences/models.py:449 +#: preferences/models.py:446 msgid "reminders" msgstr "rappels" -#: preferences/models.py:470 +#: preferences/models.py:467 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -552,7 +552,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:478 +#: preferences/models.py:475 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -560,75 +560,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:493 +#: preferences/models.py:490 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:494 +#: preferences/models.py:491 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:514 +#: preferences/models.py:511 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:515 preferences/views.py:231 +#: preferences/models.py:512 preferences/views.py:231 msgid "service" msgstr "service" -#: preferences/models.py:516 +#: preferences/models.py:513 msgid "services" msgstr "services" -#: preferences/models.py:526 +#: preferences/models.py:523 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:532 +#: preferences/models.py:529 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:542 +#: preferences/models.py:539 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:544 +#: preferences/models.py:541 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:545 +#: preferences/models.py:542 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:553 preferences/views.py:635 +#: preferences/models.py:550 preferences/views.py:635 msgid "mandate" msgstr "mandat" -#: preferences/models.py:554 +#: preferences/models.py:551 msgid "mandates" msgstr "mandats" -#: preferences/models.py:555 +#: preferences/models.py:552 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:562 +#: preferences/models.py:559 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:563 +#: preferences/models.py:560 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:565 +#: preferences/models.py:562 msgid "start date" msgstr "date de début" -#: preferences/models.py:566 +#: preferences/models.py:563 msgid "end date" msgstr "date de fin" -#: preferences/models.py:580 +#: preferences/models.py:577 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -636,140 +636,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:596 +#: preferences/models.py:593 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:599 +#: preferences/models.py:596 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:600 +#: preferences/models.py:597 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:603 +#: preferences/models.py:600 msgid "Organisation" msgstr "Association" -#: preferences/models.py:610 +#: preferences/models.py:607 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:611 +#: preferences/models.py:608 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:629 +#: preferences/models.py:626 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:630 +#: preferences/models.py:627 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:644 +#: preferences/models.py:641 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:647 +#: preferences/models.py:644 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:652 +#: preferences/models.py:649 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:654 +#: preferences/models.py:651 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:659 +#: preferences/models.py:656 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:660 +#: preferences/models.py:657 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:664 preferences/views.py:588 +#: preferences/models.py:661 preferences/views.py:588 msgid "attribute" msgstr "attribut" -#: preferences/models.py:665 +#: preferences/models.py:662 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:667 +#: preferences/models.py:664 msgid "value" msgstr "valeur" -#: preferences/models.py:669 +#: preferences/models.py:666 msgid "comment" msgstr "commentaire" -#: preferences/models.py:670 +#: preferences/models.py:667 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:681 +#: preferences/models.py:678 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:682 -#: preferences/templates/preferences/display_preferences.html:301 +#: preferences/models.py:679 +#: preferences/templates/preferences/display_preferences.html:299 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:693 +#: preferences/models.py:690 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:694 +#: preferences/models.py:691 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:703 +#: preferences/models.py:700 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:711 +#: preferences/models.py:708 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:712 +#: preferences/models.py:709 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:718 +#: preferences/models.py:715 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:719 +#: preferences/models.py:716 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:725 +#: preferences/models.py:722 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:733 +#: preferences/models.py:730 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:734 +#: preferences/models.py:731 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:740 +#: preferences/models.py:737 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:741 +#: preferences/models.py:738 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:748 +#: preferences/models.py:745 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -777,87 +777,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:758 +#: preferences/models.py:755 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:759 +#: preferences/models.py:756 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:765 +#: preferences/models.py:762 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:766 +#: preferences/models.py:763 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:772 +#: preferences/models.py:769 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:780 +#: preferences/models.py:777 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:781 +#: preferences/models.py:778 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:787 +#: preferences/models.py:784 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:788 +#: preferences/models.py:785 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:794 +#: preferences/models.py:791 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:802 +#: preferences/models.py:799 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:803 +#: preferences/models.py:800 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:809 +#: preferences/models.py:806 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:810 +#: preferences/models.py:807 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:823 +#: preferences/models.py:820 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:824 +#: preferences/models.py:821 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:851 +#: preferences/models.py:848 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:855 +#: preferences/models.py:852 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:862 +#: preferences/models.py:859 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:868 +#: preferences/models.py:865 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:870 +#: preferences/models.py:867 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -865,19 +865,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:882 +#: preferences/models.py:879 msgid "template" msgstr "modèle" -#: preferences/models.py:883 +#: preferences/models.py:880 msgid "name" msgstr "nom" -#: preferences/models.py:886 +#: preferences/models.py:883 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:887 +#: preferences/models.py:884 msgid "document templates" msgstr "modèles de document" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 92498917..942bab4a 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 0c644ed6..d9828af5 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 89d40143..5dae6c1d 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 415c618f..4ac73acc 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 03:10+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,65 +30,65 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/forms.py:76 +#: tickets/forms.py:78 msgid "comment" msgstr "commentaire" -#: tickets/models.py:57 +#: tickets/models.py:58 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:66 +#: tickets/models.py:67 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:70 +#: tickets/models.py:71 msgid "Language of the ticket." msgstr "Langue des tickets" -#: tickets/models.py:74 tickets/models.py:170 +#: tickets/models.py:76 tickets/models.py:173 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:75 tickets/models.py:171 +#: tickets/models.py:77 tickets/models.py:174 msgid "ticket" msgstr "ticket" -#: tickets/models.py:76 tickets/models.py:172 +#: tickets/models.py:78 tickets/models.py:175 msgid "tickets" msgstr "tickets" -#: tickets/models.py:80 +#: tickets/models.py:82 #, python-format msgid "Ticket from %(name)s. Date: %(date)s." msgstr "Ticket de %(name)s. Date : %(date)s." -#: tickets/models.py:82 +#: tickets/models.py:84 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:90 tickets/templates/tickets/aff_ticket.html:52 +#: tickets/models.py:92 tickets/templates/tickets/aff_ticket.html:52 msgid "Anonymous user" msgstr "Utilisateur anonyme" -#: tickets/models.py:128 +#: tickets/models.py:130 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:140 tickets/models.py:214 +#: tickets/models.py:142 tickets/models.py:217 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." -#: tickets/models.py:187 +#: tickets/models.py:190 msgid "You don't have the right to view other tickets comments than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:202 +#: tickets/models.py:205 msgid "You don't have the right to edit other tickets comments than yours." msgstr "Vous n'avez pas le droit d'éditer d'autres tickets que les vôtres." -#: tickets/models.py:232 +#: tickets/models.py:236 msgid "Update of your ticket" msgstr "Mise à jour de votre ticket" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index a637304c..53a55c1a 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 6ce34b36..fce3163a 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-22 19:00+0200\n" +"POT-Creation-Date: 2020-04-23 14:44+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" From ab0ce03b818f04a98388cd618e6c17190d3766cd Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 14:11:38 +0000 Subject: [PATCH 188/490] Add detailed history for Adherent and Club objects --- logs/models.py | 67 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/logs/models.py b/logs/models.py index 2eb5b4ca..37106df5 100644 --- a/logs/models.py +++ b/logs/models.py @@ -29,6 +29,9 @@ from machines.models import IpList from machines.models import Interface from machines.models import Machine from users.models import User +from users.models import Adherent +from users.models import Club +from topologie.models import Room class MachineHistoryEvent: @@ -245,7 +248,7 @@ class UserHistoryEvent: groups.append(Group.objects.get(id=gid).name) except Group.DoesNotExist: # TODO: Find the group name in the versions? - groups.append(_("Deleted")) + groups.append("{} ({})".format(_("Deleted"), gid)) return ", ".join(groups) elif name == "state": @@ -258,13 +261,36 @@ class UserHistoryEvent: return User.EMAIL_STATES[value][1] else: return _("Unknown") + elif name == "room_id" and value is not None: + # Try to get the room name, if it's not deleted + try: + return Room.objects.get(id=value) + except Room.DoesNotExist: + # TODO: Find the room name in the versions? + return "{} ({})".format(_("Deleted"), value) + elif name == "members" or name == "administrators": + if len(value) == 0: + # Removed all the club's members + return _("None") + + # value is a list of ints + users = [] + for uid in value: + # Try to get the user's name, if theyr're not deleted + try: + users.append(User.objects.get(id=uid).pseudo) + except User.DoesNotExist: + # TODO: Find the user's name in the versions? + groups.append("{} ({})".format(_("Deleted"), uid)) + + return ", ".join(users) if value is None: return _("None") return value - def edits(self, hide=["password", "pwd_ntlm"]): + def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]): """ Build a list of the changes performed during this event :param hide: list, the list of fields for which not to show details @@ -285,6 +311,18 @@ class UserHistoryEvent: return edits + def __eq__(self, other): + return ( + self.user.id == other.user.id + and self.edited_fields == other.edited_fields + and self.date == other.date + and self.performed_by == other.performed_by + and self.comment == other.comment + ) + + def __hash__(self): + return hash((self.user.id, frozenset(self.edited_fields), self.date, self.performed_by, self.comment)) + def __repr__(self): return "{} edited fields {} of {} ({})".format( self.performed_by, @@ -306,7 +344,14 @@ class UserHistory: """ self.events = [] + # Find whether this is a Club or an Adherent + try: + obj = Adherent.objects.get(user_ptr_id=user.id) + except Adherent.DoesNotExist: + obj = Club.objects.get(user_ptr_id=user.id) + # Get all the versions for this user, with the oldest first + self.__last_version = None user_versions = filter( lambda x: x.field_dict["id"] == user.id, Version.objects.get_for_model(User).order_by("revision__date_created") @@ -315,7 +360,23 @@ class UserHistory: for version in user_versions: self.__add_revision(user, version) - return self.events[::-1] + # Do the same thing for the Adherent of Club + self.__last_version = None + obj_versions = filter( + lambda x: x.field_dict["id"] == obj.id, + Version.objects.get_for_model(type(obj)).order_by("revision__date_created") + ) + + for version in obj_versions: + self.__add_revision(user, version) + + # Remove duplicates and sort + self.events = list(dict.fromkeys(self.events)) + return sorted( + self.events, + key=lambda e: e.date, + reverse=True + ) def __compute_diff(self, v1, v2, ignoring=["last_login", "pwd_ntlm", "email_change_date"]): """ From a25637bb35061fe7be15d7fb103c4cb853e56001 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 14:13:58 +0000 Subject: [PATCH 189/490] Fix typo in UserHistoryEvent.__repr --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index 37106df5..2066c77e 100644 --- a/logs/models.py +++ b/logs/models.py @@ -281,7 +281,7 @@ class UserHistoryEvent: users.append(User.objects.get(id=uid).pseudo) except User.DoesNotExist: # TODO: Find the user's name in the versions? - groups.append("{} ({})".format(_("Deleted"), uid)) + users.append("{} ({})".format(_("Deleted"), uid)) return ", ".join(users) From 17ee6a6caa834598736d92c6db987cc667b064ea Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 17:14:42 +0200 Subject: [PATCH 190/490] Rename MachineHistory to MachineHistorySearch --- logs/forms.py | 6 +++--- logs/models.py | 16 ++++++++-------- logs/views.py | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index 3182ef17..07e421c9 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -18,7 +18,7 @@ # 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""" +"""The forms used by the machine search view""" from django import forms from django.forms import Form @@ -31,7 +31,7 @@ CHOICES_TYPE = ( ) -class MachineHistoryForm(Form): +class MachineHistorySearchForm(Form): """The form for a simple search""" q = forms.CharField( @@ -46,7 +46,7 @@ class MachineHistoryForm(Form): e = forms.DateField(required=False, label=_("End date")) def __init__(self, *args, **kwargs): - super(MachineHistoryForm, self).__init__(*args, **kwargs) + super(MachineHistorySearchForm, self).__init__(*args, **kwargs) self.fields["s"].help_text = get_input_formats_help_text( self.fields["s"].input_formats ) diff --git a/logs/models.py b/logs/models.py index 2066c77e..2161ba9e 100644 --- a/logs/models.py +++ b/logs/models.py @@ -34,7 +34,7 @@ from users.models import Club from topologie.models import Room -class MachineHistoryEvent: +class MachineHistorySearchEvent: def __init__(self, user, machine, interface, start=None, end=None): """ :param user: User, The user owning the maching at the time of the event @@ -76,7 +76,7 @@ class MachineHistoryEvent: ) -class MachineHistory: +class MachineHistorySearch: def __init__(self): self.events = [] self.__last_evt = None @@ -85,7 +85,7 @@ class MachineHistory: """ :param search: ip or mac to lookup :param params: dict built by the search view - :return: list or None, a list of MachineHistoryEvent + :return: list or None, a list of MachineHistorySearchEvent in reverse chronological order """ self.start = params.get("s", None) self.end = params.get("e", None) @@ -93,9 +93,9 @@ class MachineHistory: self.events = [] if search_type == "ip": - return self.__get_by_ip(search) + return self.__get_by_ip(search)[::-1] elif search_type == "mac": - return self.__get_by_mac(search) + return self.__get_by_mac(search)[::-1] return None @@ -106,7 +106,7 @@ class MachineHistory: :param machine: Version, the machine version related to the interface :param interface: Version, the interface targeted by this event """ - evt = MachineHistoryEvent(user, machine, interface) + evt = MachineHistorySearchEvent(user, machine, interface) evt.start_date = interface.revision.date_created # Try not to recreate events if it's unnecessary @@ -182,7 +182,7 @@ class MachineHistory: def __get_by_ip(self, ip): """ :param ip: str, The IP to lookup - :returns: list, a list of MachineHistoryEvent + :returns: list, a list of MachineHistorySearchEvent """ interfaces = self.__get_interfaces_for_ip(ip) @@ -198,7 +198,7 @@ class MachineHistory: def __get_by_mac(self, mac): """ :param mac: str, The MAC address to lookup - :returns: list, a list of MachineHistoryEvent + :returns: list, a list of MachineHistorySearchEvent """ interfaces = self.__get_interfaces_for_mac(mac) diff --git a/logs/views.py b/logs/views.py index 25ee7403..9aaa1e03 100644 --- a/logs/views.py +++ b/logs/views.py @@ -101,8 +101,8 @@ from re2o.utils import ( from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view -from .models import MachineHistory, UserHistory -from .forms import MachineHistoryForm +from .models import MachineHistorySearch, UserHistory +from .forms import MachineHistorySearchForm @login_required @@ -486,9 +486,9 @@ def stats_actions(request): def stats_search_machine_history(request): """View which displays the history of machines with the given une IP or MAC adresse""" - history_form = MachineHistoryForm(request.GET or None) + history_form = MachineHistorySearchForm(request.GET or None) if history_form.is_valid(): - history = MachineHistory() + history = MachineHistorySearch() events = history.get( history_form.cleaned_data.get("q", ""), history_form.cleaned_data From 9a358dae424bc6202f6ff0904fdb681aecfc6f20 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 17:39:45 +0200 Subject: [PATCH 191/490] Create InterfaceHistory --- logs/models.py | 164 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 123 insertions(+), 41 deletions(-) diff --git a/logs/models.py b/logs/models.py index 2161ba9e..ccf00741 100644 --- a/logs/models.py +++ b/logs/models.py @@ -212,7 +212,77 @@ class MachineHistorySearch: return self.events -class UserHistoryEvent: +class HistoryEvent: + def __init__(self, version, previous_version=None, edited_fields=None): + """ + :param version: Version, the version of the object for this event + :param previous_version: Version, the version of the object before this event + :param edited_fields: list, The list of modified fields by this event + """ + self.version = version + self.previous_version = previous_version + self.edited_fields = edited_fields + self.date = version.revision.date_created + self.performed_by = version.revision.user + self.comment = version.revision.get_comment() or None + + def __repr(self, name, value): + """ + Returns the best representation of the given field + :param name: the name of the field + :param value: the value of the field + :return: object + """ + if value is None: + return _("None") + + return value + + def edits(self, hide=[]): + """ + Build a list of the changes performed during this event + :param hide: list, the list of fields for which not to show details + :return: str + """ + edits = [] + + for field in self.edited_fields: + if field in hide: + # Don't show sensitive information + edits.append((field, None, None)) + else: + edits.append(( + field, + self.__repr(field, self.previous_version.field_dict[field]), + self.__repr(field, self.version.field_dict[field]) + )) + + return edits + + +class History: + def __init__(self): + self.events = [] + self.__last_version = None + + def __compute_diff(self, v1, v2, ignoring=[]): + """ + Find the edited field between two versions + :param v1: Version + :param v2: Version + :param ignoring: List, a list of fields to ignore + :return: List of field names + """ + fields = [] + + for key in v1.field_dict.keys(): + if key not in ignoring and v1.field_dict[key] != v2.field_dict[key]: + fields.append(key) + + return fields + + +class UserHistoryEvent(HistoryEvent): def __init__(self, user, version, previous_version=None, edited_fields=None): """ :param user: User, The user who's history is being built @@ -220,13 +290,8 @@ class UserHistoryEvent: :param previous_version: Version, the version of the user before this event :param edited_fields: list, The list of modified fields by this event """ + super(UserHistoryEvent, self).init(version, previous_version, edited_fields) self.user = user - self.version = version - self.previous_version = previous_version - self.edited_fields = edited_fields - self.date = version.revision.date_created - self.performed_by = version.revision.user - self.comment = version.revision.get_comment() or None def __repr(self, name, value): """ @@ -296,20 +361,7 @@ class UserHistoryEvent: :param hide: list, the list of fields for which not to show details :return: str """ - edits = [] - - for field in self.edited_fields: - if field in hide: - # Don't show sensitive information - edits.append((field, None, None)) - else: - edits.append(( - field, - self.__repr(field, self.previous_version.field_dict[field]), - self.__repr(field, self.version.field_dict[field]) - )) - - return edits + return super(UserHistoryEvent, self).edits(hide) def __eq__(self, other): return ( @@ -332,10 +384,9 @@ class UserHistoryEvent: ) -class UserHistory: +class UserHistory(History): def __init__(self): - self.events = [] - self.__last_version = None + super(UserHistory, self).init() def get(self, user): """ @@ -378,22 +429,6 @@ class UserHistory: reverse=True ) - def __compute_diff(self, v1, v2, ignoring=["last_login", "pwd_ntlm", "email_change_date"]): - """ - Find the edited field between two versions - :param v1: Version - :param v2: Version - :param ignoring: List, a list of fields to ignore - :return: List of field names - """ - fields = [] - - for key in v1.field_dict.keys(): - if key not in ignoring and v1.field_dict[key] != v2.field_dict[key]: - fields.append(key) - - return fields - def __add_revision(self, user, version): """ Add a new revision to the chronological order @@ -402,7 +437,11 @@ class UserHistory: """ diff = None if self.__last_version is not None: - diff = self.__compute_diff(version, self.__last_version) + diff = self.__compute_diff( + version, + self.__last_version, + ignoring=["last_login", "pwd_ntlm", "email_change_date"] + ) # Ignore "empty" events like login if not diff: @@ -412,3 +451,46 @@ class UserHistory: evt = UserHistoryEvent(user, version, self.__last_version, diff) self.events.append(evt) self.__last_version = version + + +class InterfaceHistoryEvent(HistoryEvent): + pass + + +class InterfaceHistory: + def get(self, interface_id): + """ + :param interface_id: Interface, the interface to lookup + :return: list or None, a list of InterfaceHistoryEvent, in reverse chronological order + """ + self.events = [] + + # Get all the versions for this interface, with the oldest first + self.__last_version = None + user_versions = filter( + lambda x: x.field_dict["id"] == interface_id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + for version in user_versions: + self.__add_revision(version) + + return self.events[::-1] + + def __add_revision(self, version): + """ + Add a new revision to the chronological order + :param version: Version, The version of the interface for this event + """ + diff = None + if self.__last_version is not None: + diff = self.__compute_diff(version, self.__last_version) + + # Ignore "empty" events + if not diff: + self.__last_version = version + return + + evt = InterfaceHistoryEvent(version, self.__last_version, diff) + self.events.append(evt) + self.__last_version = version From d8ba042497bf76232e3bcf78333064817ef30f15 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 15:46:30 +0000 Subject: [PATCH 192/490] Fix history inheritence --- logs/models.py | 100 ++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/logs/models.py b/logs/models.py index ccf00741..0c52f885 100644 --- a/logs/models.py +++ b/logs/models.py @@ -79,7 +79,7 @@ class MachineHistorySearchEvent: class MachineHistorySearch: def __init__(self): self.events = [] - self.__last_evt = None + self._last_evt = None def get(self, search, params): """ @@ -93,13 +93,13 @@ class MachineHistorySearch: self.events = [] if search_type == "ip": - return self.__get_by_ip(search)[::-1] + return self._get_by_ip(search)[::-1] elif search_type == "mac": - return self.__get_by_mac(search)[::-1] + return self._get_by_mac(search)[::-1] return None - def __add_revision(self, user, machine, interface): + def _add_revision(self, user, machine, interface): """ Add a new revision to the chronological order :param user: User, The user owning the maching at the time of the event @@ -110,16 +110,16 @@ class MachineHistorySearch: evt.start_date = interface.revision.date_created # Try not to recreate events if it's unnecessary - if evt.is_similar(self.__last_evt): + if evt.is_similar(self._last_evt): return # Mark the end of validity of the last element - if self.__last_evt and not self.__last_evt.end_date: - self.__last_evt.end_date = evt.start_date + if self._last_evt and not self._last_evt.end_date: + self._last_evt.end_date = evt.start_date # If the event ends before the given date, remove it if self.start and evt.start_date.date() < self.start: - self.__last_evt = None + self._last_evt = None self.events.pop() # Make sure the new event starts before the given end date @@ -128,9 +128,9 @@ class MachineHistorySearch: # Save the new element self.events.append(evt) - self.__last_evt = evt + self._last_evt = evt - def __get_interfaces_for_ip(self, ip): + def _get_interfaces_for_ip(self, ip): """ :param ip: str :return: An iterable object with the Version objects @@ -147,7 +147,7 @@ class MachineHistorySearch: Version.objects.get_for_model(Interface).order_by("revision__date_created") ) - def __get_interfaces_for_mac(self, mac): + def _get_interfaces_for_mac(self, mac): """ :param mac: str :return: An iterable object with the Version objects @@ -158,7 +158,7 @@ class MachineHistorySearch: Version.objects.get_for_model(Interface).order_by("revision__date_created") ) - def __get_machines_for_interface(self, interface): + def _get_machines_for_interface(self, interface): """ :param interface: Version, the interface for which to find the machines :return: An iterable object with the Version objects of Machine to @@ -170,7 +170,7 @@ class MachineHistorySearch: Version.objects.get_for_model(Machine).order_by("revision__date_created") ) - def __get_user_for_machine(self, machine): + def _get_user_for_machine(self, machine): """ :param machine: Version, the machine of which the owner must be found :return: The user to which the given machine belongs @@ -179,35 +179,35 @@ class MachineHistorySearch: user_id = machine.field_dict["user_id"] return User.objects.get(id=user_id) - def __get_by_ip(self, ip): + def _get_by_ip(self, ip): """ :param ip: str, The IP to lookup :returns: list, a list of MachineHistorySearchEvent """ - interfaces = self.__get_interfaces_for_ip(ip) + interfaces = self._get_interfaces_for_ip(ip) for interface in interfaces: - machines = self.__get_machines_for_interface(interface) + machines = self._get_machines_for_interface(interface) for machine in machines: - user = self.__get_user_for_machine(machine) - self.__add_revision(user, machine, interface) + user = self._get_user_for_machine(machine) + self._add_revision(user, machine, interface) return self.events - def __get_by_mac(self, mac): + def _get_by_mac(self, mac): """ :param mac: str, The MAC address to lookup :returns: list, a list of MachineHistorySearchEvent """ - interfaces = self.__get_interfaces_for_mac(mac) + interfaces = self._get_interfaces_for_mac(mac) for interface in interfaces: - machines = self.__get_machines_for_interface(interface) + machines = self._get_machines_for_interface(interface) for machine in machines: - user = self.__get_user_for_machine(machine) - self.__add_revision(user, machine, interface) + user = self._get_user_for_machine(machine) + self._add_revision(user, machine, interface) return self.events @@ -226,7 +226,7 @@ class HistoryEvent: self.performed_by = version.revision.user self.comment = version.revision.get_comment() or None - def __repr(self, name, value): + def _repr(self, name, value): """ Returns the best representation of the given field :param name: the name of the field @@ -253,8 +253,8 @@ class HistoryEvent: else: edits.append(( field, - self.__repr(field, self.previous_version.field_dict[field]), - self.__repr(field, self.version.field_dict[field]) + self._repr(field, self.previous_version.field_dict[field]), + self._repr(field, self.version.field_dict[field]) )) return edits @@ -263,9 +263,9 @@ class HistoryEvent: class History: def __init__(self): self.events = [] - self.__last_version = None + self._last_version = None - def __compute_diff(self, v1, v2, ignoring=[]): + def _compute_diff(self, v1, v2, ignoring=[]): """ Find the edited field between two versions :param v1: Version @@ -290,10 +290,10 @@ class UserHistoryEvent(HistoryEvent): :param previous_version: Version, the version of the user before this event :param edited_fields: list, The list of modified fields by this event """ - super(UserHistoryEvent, self).init(version, previous_version, edited_fields) + super(UserHistoryEvent, self).__init__(version, previous_version, edited_fields) self.user = user - def __repr(self, name, value): + def _repr(self, name, value): """ Returns the best representation of the given field :param name: the name of the field @@ -386,7 +386,7 @@ class UserHistoryEvent(HistoryEvent): class UserHistory(History): def __init__(self): - super(UserHistory, self).init() + super(UserHistory, self).__init__() def get(self, user): """ @@ -402,24 +402,24 @@ class UserHistory(History): obj = Club.objects.get(user_ptr_id=user.id) # Get all the versions for this user, with the oldest first - self.__last_version = None + self._last_version = None user_versions = filter( lambda x: x.field_dict["id"] == user.id, Version.objects.get_for_model(User).order_by("revision__date_created") ) for version in user_versions: - self.__add_revision(user, version) + self._add_revision(user, version) # Do the same thing for the Adherent of Club - self.__last_version = None + self._last_version = None obj_versions = filter( lambda x: x.field_dict["id"] == obj.id, Version.objects.get_for_model(type(obj)).order_by("revision__date_created") ) for version in obj_versions: - self.__add_revision(user, version) + self._add_revision(user, version) # Remove duplicates and sort self.events = list(dict.fromkeys(self.events)) @@ -429,28 +429,28 @@ class UserHistory(History): reverse=True ) - def __add_revision(self, user, version): + def _add_revision(self, user, version): """ Add a new revision to the chronological order :param user: User, The user displayed in this history :param version: Version, The version of the user for this event """ diff = None - if self.__last_version is not None: - diff = self.__compute_diff( + if self._last_version is not None: + diff = self._compute_diff( version, - self.__last_version, + self._last_version, ignoring=["last_login", "pwd_ntlm", "email_change_date"] ) # Ignore "empty" events like login if not diff: - self.__last_version = version + self._last_version = version return - evt = UserHistoryEvent(user, version, self.__last_version, diff) + evt = UserHistoryEvent(user, version, self._last_version, diff) self.events.append(evt) - self.__last_version = version + self._last_version = version class InterfaceHistoryEvent(HistoryEvent): @@ -466,31 +466,31 @@ class InterfaceHistory: self.events = [] # Get all the versions for this interface, with the oldest first - self.__last_version = None + self._last_version = None user_versions = filter( lambda x: x.field_dict["id"] == interface_id, Version.objects.get_for_model(Interface).order_by("revision__date_created") ) for version in user_versions: - self.__add_revision(version) + self._add_revision(version) return self.events[::-1] - def __add_revision(self, version): + def _add_revision(self, version): """ Add a new revision to the chronological order :param version: Version, The version of the interface for this event """ diff = None - if self.__last_version is not None: - diff = self.__compute_diff(version, self.__last_version) + if self._last_version is not None: + diff = self._compute_diff(version, self._last_version) # Ignore "empty" events if not diff: - self.__last_version = version + self._last_version = version return - evt = InterfaceHistoryEvent(version, self.__last_version, diff) + evt = InterfaceHistoryEvent(version, self._last_version, diff) self.events.append(evt) - self.__last_version = version + self._last_version = version From 4ce58ddb3fc3e3de4d89d6e73b18dcfe70168e01 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:06:21 +0200 Subject: [PATCH 193/490] Add view for InterfaceHistory --- ...ser_history.html => detailed_history.html} | 2 +- logs/urls.py | 6 +- logs/views.py | 90 +++++++++++++------ 3 files changed, 70 insertions(+), 28 deletions(-) rename logs/templates/logs/{user_history.html => detailed_history.html} (97%) diff --git a/logs/templates/logs/user_history.html b/logs/templates/logs/detailed_history.html similarity index 97% rename from logs/templates/logs/user_history.html rename to logs/templates/logs/detailed_history.html index ce492281..df0f0162 100644 --- a/logs/templates/logs/user_history.html +++ b/logs/templates/logs/detailed_history.html @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "History" %}{% endblock %} {% block content %} -

{% blocktrans %}History of {{ user }}{% endblocktrans %}

+

{% blocktrans %}History of {{ object }}{% endblocktrans %}

{% if events %}
{% trans "Performed by" %} {% trans "Date" %}{% trans "Diff" %}{% trans "Performed by" %}{% trans "Edited" %} {% trans "Comment" %}
- - {{ event.performed_by }} - - {{ event.date }} + {% if event.performed_by %} + + {{ event.performed_by }} + + {% else %} + {% trans "Unknown" %} + {% endif %} + {% for edit in event.edits %} - {% if edit[1] and edit[2] %} -

{{ edit[0] }}: {{ edit[1] }} ➔ {{ edit[2] }}

- {% elif edit[2] %} -

{{ edit[0] }}: {{ edit[2] }}

+ {% if edit.1 and edit.2 %} + {{ edit.0 }}: {{ edit.1 }} ➔ {{ edit.2 }}
+ {% elif edit.2 %} + {{ edit.0 }}: {{ edit.2 }}
{% else %} -

{{ edit[0] }}

+ {{ edit.0 }}
{% endif %} {% endfor %}
{% for edit in event.edits %} - {% if edit.1 and edit.2 %} - {{ edit.0 }}: {{ edit.1 }} ➔ {{ edit.2 }}
- {% elif edit.2 %} - {{ edit.0 }}: {{ edit.2 }}
+ {% if edit.1 is None and edit.2 is None %} + {{ edit.0 }}
+ {% elif edit.1 is None %} + {{ edit.0 }}: + {{ edit.2 }}
{% else %} - {{ edit.0 }}
+ {{ edit.0 }}: + {{ edit.1 }} + ➔ {{ edit.2 }}
{% endif %} {% endfor %}
diff --git a/logs/urls.py b/logs/urls.py index 562bd93c..eefe6a70 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -37,6 +37,11 @@ urlpatterns = [ views.revert_action, name="revert-action", ), + url( + r"(?P\w+)/(?P[0-9]+)$", + views.detailed_history, + name="detailed-history", + ), url(r"^stats_general/$", views.stats_general, name="stats-general"), url(r"^stats_models/$", views.stats_models, name="stats-models"), url(r"^stats_users/$", views.stats_users, name="stats-users"), @@ -47,5 +52,4 @@ urlpatterns = [ name="history", ), url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), - url(r"^user/(?P[0-9]+)$", views.user_history, name="user-history"), ] diff --git a/logs/views.py b/logs/views.py index 9aaa1e03..0bf593c2 100644 --- a/logs/views.py +++ b/logs/views.py @@ -101,7 +101,12 @@ from re2o.utils import ( from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view -from .models import MachineHistorySearch, UserHistory +from .models import ( + MachineHistorySearch, + UserHistory, + InterfaceHistory +) + from .forms import MachineHistorySearchForm @@ -508,32 +513,77 @@ def stats_search_machine_history(request): return render(request, "logs/search_machine_history.html", {"history_form": history_form}) -@login_required -@can_view(User) -def user_history(request, users, **_kwargs): - history = UserHistory() - events = history.get(users) +def get_history_object(request, model, object_name, object_id): + """Get the objet of type model with the given object_id + Handles permissions and DoesNotExist errors + """ + try: + object_name_id = object_name + "id" + kwargs = {object_name_id: object_id} + instance = model.get_instance(**kwargs) + except model.DoesNotExist: + messages.error(request, _("Nonexistent entry.")) + return False, redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + can, msg, _permissions = instance.can_view(request.user) + if not can: + messages.error( + request, msg or _("You don't have the right to access this menu.") + ) + return False, redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + + return True, instance + + +@login_required +def detailed_history(request, object_name, object_id): + """Render a detailed history for a model. + Permissions are handled by get_history_object. + """ + # Only handle objects for which a detailed view exists + if object_name == "user": + model = User + history = UserHistory() + elif object_name == "machine": + model = Machine + elif object_name == "interface": + model = Interface + history = InterfaceHistory() + else: + raise Http404(_("No model found.")) + + # Get instance and check permissions + can_view, instance = get_history_object(model, object_name, object_id) + if not can_view: + return instance + + # Generate the pagination with the objects max_result = GeneralOption.get_cached_value("pagination_number") events = re2o_paginator( request, - events, + history.get(instance), max_result ) return render( request, - "logs/user_history.html", - { "user": users, "events": events }, + "logs/detailed_history.html", + {"object": instance, "events": events}, ) +@login_required def history(request, application, object_name, object_id): """Render history for a model. The model is determined using the `HISTORY_BIND` dictionnary if none is found, raises a Http404. The view checks if the user is allowed to see the history using the `can_view` method of the model. + Permissions are handled by get_history_object. Args: request: The request sent by the user. @@ -552,23 +602,11 @@ def history(request, application, object_name, object_id): model = apps.get_model(application, object_name) except LookupError: raise Http404(_("No model found.")) - object_name_id = object_name + "id" - kwargs = {object_name_id: object_id} - try: - instance = model.get_instance(**kwargs) - except model.DoesNotExist: - messages.error(request, _("Nonexistent entry.")) - return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) - can, msg, _permissions = instance.can_view(request.user) - if not can: - messages.error( - request, msg or _("You don't have the right to access this menu.") - ) - return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) + + can_view, instance = get_history_object(model, object_name, object_id) + if not can_view: + return instance + pagination_number = GeneralOption.get_cached_value("pagination_number") reversions = Version.objects.get_for_object(instance) if hasattr(instance, "linked_objects"): From f6fdaa700751b94a1a8b5a3976a78fe674ba4280 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 16:08:29 +0000 Subject: [PATCH 194/490] Add missing parameter to get_history_object call --- logs/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/logs/views.py b/logs/views.py index 0bf593c2..4c76158c 100644 --- a/logs/views.py +++ b/logs/views.py @@ -557,7 +557,7 @@ def detailed_history(request, object_name, object_id): raise Http404(_("No model found.")) # Get instance and check permissions - can_view, instance = get_history_object(model, object_name, object_id) + can_view, instance = get_history_object(request, model, object_name, object_id) if not can_view: return instance @@ -603,7 +603,7 @@ def history(request, application, object_name, object_id): except LookupError: raise Http404(_("No model found.")) - can_view, instance = get_history_object(model, object_name, object_id) + can_view, instance = get_history_object(get_history_object, model, object_name, object_id) if not can_view: return instance From e0d0be2c9b5d00df19e29b674776955384a55b25 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 16:16:26 +0000 Subject: [PATCH 195/490] Fix InterfaceHistory.get --- logs/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/logs/models.py b/logs/models.py index 0c52f885..6e520b93 100644 --- a/logs/models.py +++ b/logs/models.py @@ -457,22 +457,22 @@ class InterfaceHistoryEvent(HistoryEvent): pass -class InterfaceHistory: - def get(self, interface_id): +class InterfaceHistory(History): + def get(self, interface): """ - :param interface_id: Interface, the interface to lookup + :param interface: Interface, the interface to lookup :return: list or None, a list of InterfaceHistoryEvent, in reverse chronological order """ self.events = [] # Get all the versions for this interface, with the oldest first self._last_version = None - user_versions = filter( - lambda x: x.field_dict["id"] == interface_id, + interface_versions = filter( + lambda x: x.field_dict["id"] == interface.id, Version.objects.get_for_model(Interface).order_by("revision__date_created") ) - for version in user_versions: + for version in interface_versions: self._add_revision(version) return self.events[::-1] From 9725da0307ffd35b9c315b817668d35066262fc7 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:27:42 +0200 Subject: [PATCH 196/490] Add pretty representation of objects in InterfaceHistory --- logs/models.py | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/logs/models.py b/logs/models.py index 6e520b93..f6c9a10c 100644 --- a/logs/models.py +++ b/logs/models.py @@ -28,10 +28,12 @@ from django.contrib.auth.models import Group from machines.models import IpList from machines.models import Interface from machines.models import Machine +from machines.models import MachineType from users.models import User from users.models import Adherent from users.models import Club from topologie.models import Room +from topologie.models import Port class MachineHistorySearchEvent: @@ -350,10 +352,7 @@ class UserHistoryEvent(HistoryEvent): return ", ".join(users) - if value is None: - return _("None") - - return value + return super(UserHistoryEvent, self)._repr(name, value) def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]): """ @@ -477,6 +476,41 @@ class InterfaceHistory(History): return self.events[::-1] + def _repr(self, name, value): + """ + Returns the best representation of the given field + :param name: the name of the field + :param value: the value of the field + :return: object + """ + if name == "ipv4_id" and value is not None: + try: + return IpList.objects.get(id=value) + except IpList.DoesNotExist: + return "{} ({})".format(_("Deleted"), value) + elif name == "machine_type_id": + try: + return MachineType.objects.get(id=value).name + except MachineType.DoesNotExist: + return "{} ({})".format(_("Deleted"), value) + elif name == "machine_id": + try: + return Machine.objects.get(id=value).get_name() or _("No name") + except Machine.DoesNotExist: + return "{} ({})".format(_("Deleted"), value) + elif name == "port_lists": + if len(value) == 0: + return _("None") + + ports = [] + for pid in value: + try: + ports.append(Port.objects.get(id=pid).pretty_name()) + except Group.DoesNotExist: + ports.append("{} ({})".format(_("Deleted"), pid)) + + return super(UserHistoryEvent, self)._repr(name, value) + def _add_revision(self, version): """ Add a new revision to the chronological order From 4f9bf2d211fca9959981aff703392d90b5a81775 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:29:22 +0200 Subject: [PATCH 197/490] Fix _repr definition for InterfaceHistoryEvent --- logs/models.py | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/logs/models.py b/logs/models.py index f6c9a10c..53071637 100644 --- a/logs/models.py +++ b/logs/models.py @@ -453,29 +453,6 @@ class UserHistory(History): class InterfaceHistoryEvent(HistoryEvent): - pass - - -class InterfaceHistory(History): - def get(self, interface): - """ - :param interface: Interface, the interface to lookup - :return: list or None, a list of InterfaceHistoryEvent, in reverse chronological order - """ - self.events = [] - - # Get all the versions for this interface, with the oldest first - self._last_version = None - interface_versions = filter( - lambda x: x.field_dict["id"] == interface.id, - Version.objects.get_for_model(Interface).order_by("revision__date_created") - ) - - for version in interface_versions: - self._add_revision(version) - - return self.events[::-1] - def _repr(self, name, value): """ Returns the best representation of the given field @@ -509,7 +486,28 @@ class InterfaceHistory(History): except Group.DoesNotExist: ports.append("{} ({})".format(_("Deleted"), pid)) - return super(UserHistoryEvent, self)._repr(name, value) + return super(InterfaceHistoryEvent, self)._repr(name, value) + + +class InterfaceHistory(History): + def get(self, interface): + """ + :param interface: Interface, the interface to lookup + :return: list or None, a list of InterfaceHistoryEvent, in reverse chronological order + """ + self.events = [] + + # Get all the versions for this interface, with the oldest first + self._last_version = None + interface_versions = filter( + lambda x: x.field_dict["id"] == interface.id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + ) + + for version in interface_versions: + self._add_revision(version) + + return self.events[::-1] def _add_revision(self, version): """ From 9d925c4b17a0bf6ed3ff892b7286215a301bb54c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 16:35:22 +0000 Subject: [PATCH 198/490] Fix backward compatibility for logs view --- logs/urls.py | 10 +++++----- logs/views.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/logs/urls.py b/logs/urls.py index eefe6a70..914761bf 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -37,6 +37,11 @@ urlpatterns = [ views.revert_action, name="revert-action", ), + url( + r"(?P\w+)/(?P\w+)/(?P[0-9]+)$", + views.history, + name="history", + ), url( r"(?P\w+)/(?P[0-9]+)$", views.detailed_history, @@ -46,10 +51,5 @@ urlpatterns = [ url(r"^stats_models/$", views.stats_models, name="stats-models"), url(r"^stats_users/$", views.stats_users, name="stats-users"), url(r"^stats_actions/$", views.stats_actions, name="stats-actions"), - url( - r"(?P\w+)/(?P\w+)/(?P[0-9]+)$", - views.history, - name="history", - ), url(r"^stats_search_machine/$", views.stats_search_machine_history, name="stats-search-machine"), ] diff --git a/logs/views.py b/logs/views.py index 4c76158c..153bfb34 100644 --- a/logs/views.py +++ b/logs/views.py @@ -603,7 +603,7 @@ def history(request, application, object_name, object_id): except LookupError: raise Http404(_("No model found.")) - can_view, instance = get_history_object(get_history_object, model, object_name, object_id) + can_view, instance = get_history_object(request, model, object_name, object_id) if not can_view: return instance From ac53084695c435ec9dca6020e3f0d973fd43d69b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:43:34 +0200 Subject: [PATCH 199/490] Improve handling of detailed history button --- logs/templatetags/logs_extra.py | 3 ++- machines/templates/machines/aff_machines.html | 4 ++-- templates/buttons/history.html | 4 ++-- users/templates/users/profil.html | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index c436c1fa..2e58cb67 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -42,7 +42,7 @@ def is_facture(baseinvoice): @register.inclusion_tag("buttons/history.html") -def history_button(instance, text=False, html_class=True): +def history_button(instance, text=False, detailed=False, html_class=True): """Creates the correct history button for an instance. Args: @@ -57,5 +57,6 @@ def history_button(instance, text=False, html_class=True): "name": instance._meta.model_name, "id": instance.id, "text": text, + "detailed": detailed, "class": html_class, } diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 77b65546..857d2a3a 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -67,7 +67,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Create an interface" as tr_create_an_interface %} {% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %} {% acl_end %} - {% history_button machine %} + {% history_button machine detailed=True %} {% can_delete machine %} {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %} {% acl_end %} @@ -161,7 +161,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} - {% history_button interface %} + {% history_button interface detailed=True %} {% can_delete interface %} {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %} {% acl_end %} diff --git a/templates/buttons/history.html b/templates/buttons/history.html index f8eed813..04b0178f 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} -{% if name == "user" %} - +{% if detailed %} + {% if text %}{% trans "History" %}{% endif %} {% else %} diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 4fec3c18..4fa780a7 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -176,7 +176,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit the groups" %} {% acl_end %} - {% history_button users text=True %} + {% history_button users text=True detailed=True %}
From bf0fa33c5fcb2085001311becfc10097c329a1e7 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:44:44 +0200 Subject: [PATCH 200/490] Add missing parameter to detailed-history call --- templates/buttons/history.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/buttons/history.html b/templates/buttons/history.html index 04b0178f..2495dec8 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} {% if detailed %} - + {% if text %}{% trans "History" %}{% endif %} {% else %} From 875a43ed017bc2baaa187f0ccfb36ce289ea5a1e Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 19:06:59 +0200 Subject: [PATCH 201/490] Handle machine history --- logs/models.py | 141 ++++++++++++++++++++++++++++++++++++------------- logs/views.py | 2 + 2 files changed, 107 insertions(+), 36 deletions(-) diff --git a/logs/models.py b/logs/models.py index 53071637..b6ddc692 100644 --- a/logs/models.py +++ b/logs/models.py @@ -214,6 +214,18 @@ class MachineHistorySearch: return self.events +class RelatedHistory: + def __init__(self, model_name, object_id, detailed=True): + """ + :param model_name: Name of the related model (e.g. "user") + :param object_id: ID of the related object + :param detailed: Whether the related history should be shown in an detailed view + """ + self.model_name = model_name + self.object_id = object_id + self.detailed = detailed + + class HistoryEvent: def __init__(self, version, previous_version=None, edited_fields=None): """ @@ -265,7 +277,28 @@ class HistoryEvent: class History: def __init__(self): self.events = [] + self.related = [] # For example, a machine has a list of its interfaces self._last_version = None + self.event_type = HistoryEvent + + def get(self, instance): + """ + :param interface: The instance to lookup + :return: list or None, a list of HistoryEvent, in reverse chronological order + """ + self.events = [] + + # Get all the versions for this interface, with the oldest first + self._last_version = None + interface_versions = filter( + lambda x: x.field_dict["id"] == instance.id, + Version.objects.get_for_model(type(instance)).order_by("revision__date_created") + ) + + for version in interface_versions: + self._add_revision(version) + + return self.events[::-1] def _compute_diff(self, v1, v2, ignoring=[]): """ @@ -283,6 +316,24 @@ class History: return fields + def _add_revision(self, version): + """ + Add a new revision to the chronological order + :param version: Version, The version of the interface for this event + """ + diff = None + if self._last_version is not None: + diff = self._compute_diff(version, self._last_version) + + # Ignore "empty" events + if not diff: + self._last_version = version + return + + evt = self.event_type(version, self._last_version, diff) + self.events.append(evt) + self._last_version = version + class UserHistoryEvent(HistoryEvent): def __init__(self, user, version, previous_version=None, edited_fields=None): @@ -386,6 +437,7 @@ class UserHistoryEvent(HistoryEvent): class UserHistory(History): def __init__(self): super(UserHistory, self).__init__() + self.event_type = UserHistoryEvent def get(self, user): """ @@ -400,6 +452,17 @@ class UserHistory(History): except Adherent.DoesNotExist: obj = Club.objects.get(user_ptr_id=user.id) + # Add as "related" histories the list of Machine objects + # that were once owned by this user + self.related = list(filter( + lambda x: x.field_dict["user_id"] == user.id, + Version.objects.get_for_model(Machine).order_by("revision__date_created") + )) + self.related = sorted( + list(dict.fromkeys(self.related)), + key=lambda r: r.model_name + ) + # Get all the versions for this user, with the oldest first self._last_version = None user_versions = filter( @@ -452,6 +515,45 @@ class UserHistory(History): self._last_version = version +class MachineHistoryEvent(HistoryEvent): + def _repr(self, name, value): + """ + Returns the best representation of the given field + :param name: the name of the field + :param value: the value of the field + :return: object + """ + if name == "user_id": + try: + return User.objects.get(id=value).pseudo + except User.DoesNotExist: + return "{} ({})".format(_("Deleted"), value) + + return super(MachineHistoryEvent, self)._repr(name, value) + + +class MachineHistory(History): + def __init__(self): + super(MachineHistory, self).__init__() + self.event_type = MachineHistoryEvent + + def get(self, machine): + super(MachineHistory, self).get(machine) + + # Add as "related" histories the list of Interface objects + # that were once assigned to this machine + self.related = list(filter( + lambda x: x.field_dict["machine_id"] == machine.id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") + )) + + # Remove duplicates and sort + self.related = sorted( + list(dict.fromkeys(self.related)), + key=lambda r: r.model_name + ) + + class InterfaceHistoryEvent(HistoryEvent): def _repr(self, name, value): """ @@ -490,39 +592,6 @@ class InterfaceHistoryEvent(HistoryEvent): class InterfaceHistory(History): - def get(self, interface): - """ - :param interface: Interface, the interface to lookup - :return: list or None, a list of InterfaceHistoryEvent, in reverse chronological order - """ - self.events = [] - - # Get all the versions for this interface, with the oldest first - self._last_version = None - interface_versions = filter( - lambda x: x.field_dict["id"] == interface.id, - Version.objects.get_for_model(Interface).order_by("revision__date_created") - ) - - for version in interface_versions: - self._add_revision(version) - - return self.events[::-1] - - def _add_revision(self, version): - """ - Add a new revision to the chronological order - :param version: Version, The version of the interface for this event - """ - diff = None - if self._last_version is not None: - diff = self._compute_diff(version, self._last_version) - - # Ignore "empty" events - if not diff: - self._last_version = version - return - - evt = InterfaceHistoryEvent(version, self._last_version, diff) - self.events.append(evt) - self._last_version = version + def __init__(self): + super(InterfaceHistory, self).__init__() + self.event_type = InterfaceHistoryEvent diff --git a/logs/views.py b/logs/views.py index 153bfb34..a4396158 100644 --- a/logs/views.py +++ b/logs/views.py @@ -104,6 +104,7 @@ from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view from .models import ( MachineHistorySearch, UserHistory, + MachineHistory, InterfaceHistory ) @@ -550,6 +551,7 @@ def detailed_history(request, object_name, object_id): history = UserHistory() elif object_name == "machine": model = Machine + history = MachineHistory() elif object_name == "interface": model = Interface history = InterfaceHistory() From d6727d18e5e80bec70df26506de9ce95547023e9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 19:24:28 +0200 Subject: [PATCH 202/490] Displayed related history suggestions in detailed history view --- logs/models.py | 36 +++++++++++++---------- logs/templates/logs/detailed_history.html | 23 ++++++++++++++- logs/views.py | 2 +- 3 files changed, 44 insertions(+), 17 deletions(-) diff --git a/logs/models.py b/logs/models.py index b6ddc692..e48b0dd1 100644 --- a/logs/models.py +++ b/logs/models.py @@ -215,16 +215,26 @@ class MachineHistorySearch: class RelatedHistory: - def __init__(self, model_name, object_id, detailed=True): + def __init__(self, name, instance, detailed=True): """ :param model_name: Name of the related model (e.g. "user") :param object_id: ID of the related object :param detailed: Whether the related history should be shown in an detailed view """ - self.model_name = model_name - self.object_id = object_id + self.name = name + self.instance = instance self.detailed = detailed + def __eq__(self, other): + return ( + self.name == other.name + and self.instance.id == other.instance.id + and self.detailed == other.detailed + ) + + def __hash__(self): + return hash((self.name, self.instance.id, self.detailed)) + class HistoryEvent: def __init__(self, version, previous_version=None, edited_fields=None): @@ -454,14 +464,12 @@ class UserHistory(History): # Add as "related" histories the list of Machine objects # that were once owned by this user - self.related = list(filter( + self.related = filter( lambda x: x.field_dict["user_id"] == user.id, Version.objects.get_for_model(Machine).order_by("revision__date_created") - )) - self.related = sorted( - list(dict.fromkeys(self.related)), - key=lambda r: r.model_name ) + self.related = [RelatedHistory(m.get_name(), m) for m in self.related] + self.related = list(dict.fromkeys(self.related)) # Get all the versions for this user, with the oldest first self._last_version = None @@ -538,8 +546,6 @@ class MachineHistory(History): self.event_type = MachineHistoryEvent def get(self, machine): - super(MachineHistory, self).get(machine) - # Add as "related" histories the list of Interface objects # that were once assigned to this machine self.related = list(filter( @@ -547,11 +553,11 @@ class MachineHistory(History): Version.objects.get_for_model(Interface).order_by("revision__date_created") )) - # Remove duplicates and sort - self.related = sorted( - list(dict.fromkeys(self.related)), - key=lambda r: r.model_name - ) + # Create RelatedHistory objects and remove duplicates + self.related = [RelatedHistory(i.mac_address, i) for i in self.related] + self.related = list(dict.fromkeys(self.related)) + + return super(MachineHistory, self).get(machine) class InterfaceHistoryEvent(HistoryEvent): diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index df0f0162..53302451 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -1,4 +1,3 @@ -{% extends 'logs/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 @@ -73,6 +72,28 @@ with this program; if not, write to the Free Software Foundation, Inc., {% else %}

{% trans "No event" %}

{% endif %} + +

{% trans Related history %}

+ +{% if related_history %} +
+ + + + + + + {% for related in related_history %} + + + + + {% endfor %} +
{% trans "ID" %}{% trans "Actions" %}
{{ related.name }}{% history_button related.instance text=True detailed=related.detailed %}
+ {% include 'pagination.html' with list=events %} +{% else %} +

{% trans "No related history" %}

+{% endif %}


diff --git a/logs/views.py b/logs/views.py index a4396158..54432794 100644 --- a/logs/views.py +++ b/logs/views.py @@ -574,7 +574,7 @@ def detailed_history(request, object_name, object_id): return render( request, "logs/detailed_history.html", - {"object": instance, "events": events}, + {"object": instance, "events": events, "related_history": history.related}, ) From 76f93fa38363993fab3f53515b2c115879b9acc7 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 17:46:25 +0000 Subject: [PATCH 203/490] Fix dispalying related history in detailed history view --- logs/models.py | 4 ++-- logs/templates/logs/detailed_history.html | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/logs/models.py b/logs/models.py index e48b0dd1..d2f0b5a9 100644 --- a/logs/models.py +++ b/logs/models.py @@ -468,7 +468,7 @@ class UserHistory(History): lambda x: x.field_dict["user_id"] == user.id, Version.objects.get_for_model(Machine).order_by("revision__date_created") ) - self.related = [RelatedHistory(m.get_name(), m) for m in self.related] + self.related = [RelatedHistory(m.field_dict["name"] or _("None"), m) for m in self.related] self.related = list(dict.fromkeys(self.related)) # Get all the versions for this user, with the oldest first @@ -554,7 +554,7 @@ class MachineHistory(History): )) # Create RelatedHistory objects and remove duplicates - self.related = [RelatedHistory(i.mac_address, i) for i in self.related] + self.related = [RelatedHistory(i.field_dict["mac_address"], i) for i in self.related] self.related = list(dict.fromkeys(self.related)) return super(MachineHistory, self).get(machine) diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index 53302451..34da370c 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -1,3 +1,4 @@ +{% extends 'logs/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 @@ -22,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} +{% load logs_extra %} {% block title %}{% trans "History" %}{% endblock %} @@ -73,20 +75,20 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% trans "No event" %}

{% endif %} -

{% trans Related history %}

+

{% trans "Related history" %}

{% if related_history %} - + {% for related in related_history %} + - {% endfor %}
{% trans "ID" %} {% trans "Actions" %}{% trans "ID" %}
{% history_button related.instance detailed=related.detailed %} {{ related.name }}{% history_button related.instance text=True detailed=related.detailed %}
From f3ac37d2e6409a8bd61032ebaf304d7bf1108997 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 20:00:38 +0200 Subject: [PATCH 204/490] Make related history list nicer --- logs/models.py | 26 ++++++++++++++--------- logs/templates/logs/detailed_history.html | 18 +++++----------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/logs/models.py b/logs/models.py index d2f0b5a9..3b088b87 100644 --- a/logs/models.py +++ b/logs/models.py @@ -215,25 +215,25 @@ class MachineHistorySearch: class RelatedHistory: - def __init__(self, name, instance, detailed=True): + def __init__(self, name, model_name, object_id): """ + :param name: Name of this instance :param model_name: Name of the related model (e.g. "user") :param object_id: ID of the related object - :param detailed: Whether the related history should be shown in an detailed view """ - self.name = name - self.instance = instance - self.detailed = detailed + self.name = "{} (id = {})".format(name, object_id) + self.model_name = model_name + self.object_id = object_id def __eq__(self, other): return ( self.name == other.name - and self.instance.id == other.instance.id - and self.detailed == other.detailed + and self.model_name == other.model_name + and self.object_id == other.object_id ) def __hash__(self): - return hash((self.name, self.instance.id, self.detailed)) + return hash((self.name, self.model_name, self.object_id)) class HistoryEvent: @@ -468,7 +468,10 @@ class UserHistory(History): lambda x: x.field_dict["user_id"] == user.id, Version.objects.get_for_model(Machine).order_by("revision__date_created") ) - self.related = [RelatedHistory(m.field_dict["name"] or _("None"), m) for m in self.related] + self.related = [RelatedHistory( + m.field_dict["name"] or _("None"), + "machine", + m.field_dict["id"]) for m in self.related] self.related = list(dict.fromkeys(self.related)) # Get all the versions for this user, with the oldest first @@ -554,7 +557,10 @@ class MachineHistory(History): )) # Create RelatedHistory objects and remove duplicates - self.related = [RelatedHistory(i.field_dict["mac_address"], i) for i in self.related] + self.related = [RelatedHistory( + i.field_dict["mac_address"] or _("None"), + "interface", + i.field_dict["id"]) for i in self.related] self.related = list(dict.fromkeys(self.related)) return super(MachineHistory, self).get(machine) diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index 34da370c..b862448d 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -78,21 +78,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,

{% trans "Related history" %}

{% if related_history %} - - - - - - - +
    {% for related in related_history %} -
- - - +
  • + {{ related.name }} +
  • {% endfor %} -
    {% trans "Actions" %}{% trans "ID" %}
    {% history_button related.instance detailed=related.detailed %}{{ related.name }}
    - {% include 'pagination.html' with list=events %} + {% else %}

    {% trans "No related history" %}

    {% endif %} From caf09173f333714163860beb8194effd3edcc8f9 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:09:39 +0000 Subject: [PATCH 205/490] Make related history in detailed history view nicer --- logs/models.py | 9 ++++----- logs/templates/logs/detailed_history.html | 9 ++++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/logs/models.py b/logs/models.py index 3b088b87..e3f0cba8 100644 --- a/logs/models.py +++ b/logs/models.py @@ -227,13 +227,12 @@ class RelatedHistory: def __eq__(self, other): return ( - self.name == other.name - and self.model_name == other.model_name + self.model_name == other.model_name and self.object_id == other.object_id ) def __hash__(self): - return hash((self.name, self.model_name, self.object_id)) + return hash((self.model_name, self.object_id)) class HistoryEvent: @@ -466,7 +465,7 @@ class UserHistory(History): # that were once owned by this user self.related = filter( lambda x: x.field_dict["user_id"] == user.id, - Version.objects.get_for_model(Machine).order_by("revision__date_created") + Version.objects.get_for_model(Machine).order_by("-revision__date_created") ) self.related = [RelatedHistory( m.field_dict["name"] or _("None"), @@ -553,7 +552,7 @@ class MachineHistory(History): # that were once assigned to this machine self.related = list(filter( lambda x: x.field_dict["machine_id"] == machine.id, - Version.objects.get_for_model(Interface).order_by("revision__date_created") + Version.objects.get_for_model(Interface).order_by("-revision__date_created") )) # Create RelatedHistory objects and remove duplicates diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index b862448d..26bae90c 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -75,18 +75,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "No event" %}

    {% endif %} -

    {% trans "Related history" %}

    - {% if related_history %} + +

    {% blocktrans %}Related elements{% endblocktrans %}

    + -{% else %} -

    {% trans "No related history" %}

    {% endif %}

    From ead2609564a0edc85bde06c42fd90834d8927400 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 18:13:37 +0000 Subject: [PATCH 206/490] Add missing translations for detailed history --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 142 ++++++----- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 254 ++++++++++---------- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 5 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 182 +++++++------- 12 files changed, 311 insertions(+), 288 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index f5f9153f..6b5b3c75 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index ac3900ea..674a417c 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 2c5265fc..e9310b54 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -58,20 +58,26 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" -#: logs/models.py:238 logs/models.py:263 +#: logs/models.py:260 logs/models.py:368 logs/models.py:401 logs/models.py:471 +#: logs/models.py:560 logs/models.py:593 msgid "None" msgstr "Aucun(e)" -#: logs/models.py:248 +#: logs/models.py:378 logs/models.py:397 logs/models.py:411 logs/models.py:540 +#: logs/models.py:580 logs/models.py:585 logs/models.py:590 logs/models.py:600 msgid "Deleted" msgstr "Supprimé(e)" -#: logs/models.py:255 logs/models.py:260 +#: logs/models.py:385 logs/models.py:390 +#: logs/templates/logs/detailed_history.html:52 #: logs/templates/logs/machine_history.html:55 -#: logs/templates/logs/user_history.html:51 msgid "Unknown" msgstr "Inconnu(e)" +#: logs/models.py:588 +msgid "No name" +msgstr "Sans nom" + #: logs/templates/logs/aff_stats_logs.html:36 msgid "Edited object" msgstr "Objet modifié" @@ -90,8 +96,8 @@ msgid "Date of editing" msgstr "Date de modification" #: logs/templates/logs/aff_stats_logs.html:42 +#: logs/templates/logs/detailed_history.html:40 #: logs/templates/logs/machine_history.html:39 -#: logs/templates/logs/user_history.html:39 msgid "Comment" msgstr "Commentaire" @@ -128,7 +134,7 @@ msgid "Rank" msgstr "Rang" #: logs/templates/logs/aff_summary.html:37 -#: logs/templates/logs/user_history.html:36 +#: logs/templates/logs/detailed_history.html:37 msgid "Date" msgstr "Date" @@ -192,6 +198,31 @@ msgstr "" msgid "Confirm" msgstr "Confirmer" +#: logs/templates/logs/detailed_history.html:28 +#: logs/templates/logs/detailed_history.html:85 +msgid "History" +msgstr "Historique" + +#: logs/templates/logs/detailed_history.html:31 +msgid "History of %(object)s" +msgstr "Historique de %(object)s" + +#: logs/templates/logs/detailed_history.html:38 +msgid "Performed by" +msgstr "Effectué(e) par" + +#: logs/templates/logs/detailed_history.html:39 +msgid "Edited" +msgstr "Modifié" + +#: logs/templates/logs/detailed_history.html:75 +msgid "No event" +msgstr "Aucun évènement" + +#: logs/templates/logs/detailed_history.html:80 +msgid "Related elements" +msgstr "Élements liés" + #: logs/templates/logs/index.html:29 logs/templates/logs/stats_general.html:29 #: logs/templates/logs/stats_logs.html:29 #: logs/templates/logs/stats_models.html:29 @@ -200,7 +231,7 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:421 +#: logs/views.py:427 msgid "Actions performed" msgstr "Actions effectuées" @@ -265,159 +296,138 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: logs/templates/logs/user_history.html:27 -msgid "History" -msgstr "Historique" - -#: logs/templates/logs/user_history.html:30 -#, python-format -msgid "History of %(user)s" -msgstr "Historique de %(user)s" - -#: logs/templates/logs/user_history.html:37 -msgid "Performed by" -msgstr "Effectué(e) par" - -#: logs/templates/logs/user_history.html:38 -msgid "Edited" -msgstr "Modifié" - -#: logs/templates/logs/user_history.html:74 -msgid "No event" -msgstr "Aucun évènement" - -#: logs/views.py:178 +#: logs/views.py:184 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: logs/views.py:181 +#: logs/views.py:187 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: logs/views.py:222 +#: logs/views.py:228 msgid "Category" msgstr "Catégorie" -#: logs/views.py:223 +#: logs/views.py:229 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: logs/views.py:224 +#: logs/views.py:230 msgid "Number of members" msgstr "Nombre d'adhérents" -#: logs/views.py:225 +#: logs/views.py:231 msgid "Number of clubs" msgstr "Nombre de clubs" -#: logs/views.py:229 +#: logs/views.py:235 msgid "Activated users" msgstr "Utilisateurs activés" -#: logs/views.py:235 +#: logs/views.py:241 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: logs/views.py:241 +#: logs/views.py:247 msgid "Archived users" msgstr "Utilisateurs archivés" -#: logs/views.py:247 +#: logs/views.py:253 msgid "Fully archived users" msgstr "Utilisateurs complètement archivés" -#: logs/views.py:257 +#: logs/views.py:263 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:267 +#: logs/views.py:273 msgid "Contributing members" msgstr "Adhérents cotisants" -#: logs/views.py:273 +#: logs/views.py:279 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: logs/views.py:279 +#: logs/views.py:285 msgid "Banned users" msgstr "Utilisateurs bannis" -#: logs/views.py:285 +#: logs/views.py:291 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: logs/views.py:291 +#: logs/views.py:297 msgid "Users with a confirmed email" msgstr "Utilisateurs ayant un mail confirmé" -#: logs/views.py:297 +#: logs/views.py:303 msgid "Users with an unconfirmed email" msgstr "Utilisateurs ayant un mail non confirmé" -#: logs/views.py:303 +#: logs/views.py:309 msgid "Users pending email confirmation" msgstr "Utilisateurs en attente de confirmation du mail" -#: logs/views.py:309 +#: logs/views.py:315 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:323 +#: logs/views.py:329 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:340 +#: logs/views.py:346 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:341 +#: logs/views.py:347 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:342 +#: logs/views.py:348 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:343 +#: logs/views.py:349 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:344 +#: logs/views.py:350 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:345 +#: logs/views.py:351 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:360 +#: logs/views.py:366 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:406 +#: logs/views.py:412 msgid "Topology" msgstr "Topologie" -#: logs/views.py:422 +#: logs/views.py:428 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:447 +#: logs/views.py:453 msgid "rights" msgstr "droits" -#: logs/views.py:476 +#: logs/views.py:482 msgid "actions" msgstr "actions" -#: logs/views.py:554 -msgid "No model found." -msgstr "Aucun modèle trouvé." - -#: logs/views.py:560 +#: logs/views.py:526 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:567 +#: logs/views.py:534 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." + +#: logs/views.py:559 logs/views.py:606 +msgid "No model found." +msgstr "Aucun modèle trouvé." diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 5013ea49..81109ef4 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index f4daa664..026a1da2 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 2f109979..00ea2a38 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -342,13 +342,13 @@ msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:122 +#: preferences/models.py:125 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:127 +#: preferences/models.py:130 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -356,11 +356,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:131 +#: preferences/models.py:134 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:136 +#: preferences/models.py:139 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -368,7 +368,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:143 +#: preferences/models.py:146 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -379,172 +379,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:150 +#: preferences/models.py:153 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:154 +#: preferences/models.py:157 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:155 +#: preferences/models.py:158 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:162 +#: preferences/models.py:165 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:180 +#: preferences/models.py:183 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:181 +#: preferences/models.py:184 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:182 +#: preferences/models.py:185 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:191 +#: preferences/models.py:194 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:201 +#: preferences/models.py:204 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:202 +#: preferences/models.py:205 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:222 preferences/models.py:684 +#: preferences/models.py:225 preferences/models.py:687 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:223 preferences/models.py:685 +#: preferences/models.py:226 preferences/models.py:688 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:229 +#: preferences/models.py:232 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:234 +#: preferences/models.py:237 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:240 +#: preferences/models.py:243 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:247 +#: preferences/models.py:250 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:253 +#: preferences/models.py:256 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:256 +#: preferences/models.py:259 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:259 +#: preferences/models.py:262 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:364 +#: preferences/models.py:367 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:366 +#: preferences/models.py:369 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:379 +#: preferences/models.py:382 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:381 +#: preferences/models.py:384 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:384 +#: preferences/models.py:387 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:388 +#: preferences/models.py:391 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:389 preferences/views.py:335 +#: preferences/models.py:392 preferences/views.py:335 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:390 -#: preferences/templates/preferences/display_preferences.html:221 +#: preferences/models.py:393 +#: preferences/templates/preferences/display_preferences.html:223 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:397 +#: preferences/models.py:400 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:400 +#: preferences/models.py:403 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:406 +#: preferences/models.py:409 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:407 +#: preferences/models.py:410 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:409 +#: preferences/models.py:412 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:416 +#: preferences/models.py:419 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:419 preferences/views.py:397 +#: preferences/models.py:422 preferences/views.py:397 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:422 +#: preferences/models.py:425 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:434 +#: preferences/models.py:437 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:440 +#: preferences/models.py:443 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:444 +#: preferences/models.py:447 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:445 preferences/views.py:280 +#: preferences/models.py:448 preferences/views.py:280 msgid "reminder" msgstr "rappel" -#: preferences/models.py:446 +#: preferences/models.py:449 msgid "reminders" msgstr "rappels" -#: preferences/models.py:467 +#: preferences/models.py:470 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -552,7 +552,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:475 +#: preferences/models.py:478 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -560,75 +560,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:490 +#: preferences/models.py:493 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:491 +#: preferences/models.py:494 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:511 +#: preferences/models.py:514 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:512 preferences/views.py:231 +#: preferences/models.py:515 preferences/views.py:231 msgid "service" msgstr "service" -#: preferences/models.py:513 +#: preferences/models.py:516 msgid "services" msgstr "services" -#: preferences/models.py:523 +#: preferences/models.py:526 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:529 +#: preferences/models.py:532 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:539 +#: preferences/models.py:542 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:541 +#: preferences/models.py:544 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:542 +#: preferences/models.py:545 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:550 preferences/views.py:635 +#: preferences/models.py:553 preferences/views.py:635 msgid "mandate" msgstr "mandat" -#: preferences/models.py:551 +#: preferences/models.py:554 msgid "mandates" msgstr "mandats" -#: preferences/models.py:552 +#: preferences/models.py:555 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:559 +#: preferences/models.py:562 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:560 +#: preferences/models.py:563 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:562 +#: preferences/models.py:565 msgid "start date" msgstr "date de début" -#: preferences/models.py:563 +#: preferences/models.py:566 msgid "end date" msgstr "date de fin" -#: preferences/models.py:577 +#: preferences/models.py:580 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -636,140 +636,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:593 +#: preferences/models.py:596 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:596 +#: preferences/models.py:599 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:597 +#: preferences/models.py:600 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:600 +#: preferences/models.py:603 msgid "Organisation" msgstr "Association" -#: preferences/models.py:607 +#: preferences/models.py:610 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:608 +#: preferences/models.py:611 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:626 +#: preferences/models.py:629 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:627 +#: preferences/models.py:630 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:641 +#: preferences/models.py:644 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:644 +#: preferences/models.py:647 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:649 +#: preferences/models.py:652 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:651 +#: preferences/models.py:654 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:656 +#: preferences/models.py:659 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:657 +#: preferences/models.py:660 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:661 preferences/views.py:588 +#: preferences/models.py:664 preferences/views.py:588 msgid "attribute" msgstr "attribut" -#: preferences/models.py:662 +#: preferences/models.py:665 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:664 +#: preferences/models.py:667 msgid "value" msgstr "valeur" -#: preferences/models.py:666 +#: preferences/models.py:669 msgid "comment" msgstr "commentaire" -#: preferences/models.py:667 +#: preferences/models.py:670 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:678 +#: preferences/models.py:681 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:679 -#: preferences/templates/preferences/display_preferences.html:299 +#: preferences/models.py:682 +#: preferences/templates/preferences/display_preferences.html:301 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:690 +#: preferences/models.py:693 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:691 +#: preferences/models.py:694 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:700 +#: preferences/models.py:703 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:708 +#: preferences/models.py:711 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:709 +#: preferences/models.py:712 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:715 +#: preferences/models.py:718 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:716 +#: preferences/models.py:719 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:722 +#: preferences/models.py:725 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:730 +#: preferences/models.py:733 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:731 +#: preferences/models.py:734 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:737 +#: preferences/models.py:740 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:738 +#: preferences/models.py:741 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:745 +#: preferences/models.py:748 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -777,87 +777,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:755 +#: preferences/models.py:758 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:756 +#: preferences/models.py:759 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:762 +#: preferences/models.py:765 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:763 +#: preferences/models.py:766 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:769 +#: preferences/models.py:772 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:777 +#: preferences/models.py:780 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:778 +#: preferences/models.py:781 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:784 +#: preferences/models.py:787 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:785 +#: preferences/models.py:788 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:791 +#: preferences/models.py:794 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:799 +#: preferences/models.py:802 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:800 +#: preferences/models.py:803 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:806 +#: preferences/models.py:809 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:807 +#: preferences/models.py:810 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:820 +#: preferences/models.py:823 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:821 +#: preferences/models.py:824 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:848 +#: preferences/models.py:851 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:852 +#: preferences/models.py:855 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:859 +#: preferences/models.py:862 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:865 +#: preferences/models.py:868 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:867 +#: preferences/models.py:870 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -865,19 +865,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:879 +#: preferences/models.py:882 msgid "template" msgstr "modèle" -#: preferences/models.py:880 +#: preferences/models.py:883 msgid "name" msgstr "nom" -#: preferences/models.py:883 +#: preferences/models.py:886 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:884 +#: preferences/models.py:887 msgid "document templates" msgstr "modèles de document" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 942bab4a..625eda15 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index d9828af5..e33d68fd 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 5dae6c1d..7187c189 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -240,7 +240,8 @@ msgstr "" msgid "Edit" msgstr "Modifier" -#: templates/buttons/history.html:26 templates/buttons/history.html:27 +#: templates/buttons/history.html:28 templates/buttons/history.html:29 +#: templates/buttons/history.html:32 templates/buttons/history.html:33 msgid "History" msgstr "" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 4ac73acc..bb56a162 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 53a55c1a..ee5bfb93 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index fce3163a..f5029a64 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 14:44+0200\n" +"POT-Creation-Date: 2020-04-23 20:09+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -56,7 +56,7 @@ msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." #: users/forms.py:133 users/forms.py:181 users/forms.py:405 -#: users/models.py:1912 +#: users/models.py:1936 msgid "Password" msgstr "Mot de passe" @@ -109,7 +109,7 @@ msgstr "Prénom" msgid "Surname" msgstr "Nom" -#: users/forms.py:321 users/forms.py:536 users/models.py:1912 +#: users/forms.py:321 users/forms.py:536 users/models.py:1936 #: users/templates/users/aff_emailaddress.html:36 #: users/templates/users/profil.html:209 msgid "Email address" @@ -323,7 +323,7 @@ msgstr "Non confirmé" msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:204 users/models.py:1565 +#: users/models.py:204 users/models.py:1589 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." @@ -365,66 +365,72 @@ msgstr "Peut forcer le déménagement" msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:257 +#: users/models.py:255 +#, fuzzy +#| msgid "Can edit the state of a user" +msgid "Can edit the pseudo of a user" +msgstr "Peut changer l'état d'un utilisateur" + +#: users/models.py:258 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:259 +#: users/models.py:260 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:260 +#: users/models.py:261 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:262 +#: users/models.py:263 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:263 +#: users/models.py:264 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:281 users/models.py:309 users/models.py:319 +#: users/models.py:282 users/models.py:310 users/models.py:320 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:315 users/templates/users/aff_listright.html:75 +#: users/models.py:316 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:317 +#: users/models.py:318 msgid "Club" msgstr "Club" -#: users/models.py:896 +#: users/models.py:897 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:898 +#: users/models.py:899 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:921 users/templates/users/user_autocapture.html:64 +#: users/models.py:922 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: users/models.py:1019 +#: users/models.py:1020 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:1033 users/models.py:1087 +#: users/models.py:1034 users/models.py:1088 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:1045 +#: users/models.py:1046 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:1052 +#: users/models.py:1053 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -432,257 +438,263 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:1064 users/models.py:1102 +#: users/models.py:1065 users/models.py:1103 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:1128 +#: users/models.py:1129 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1145 +#: users/models.py:1146 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1165 +#: users/models.py:1166 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1182 users/models.py:1197 +#: users/models.py:1188 +#, fuzzy +#| msgid "You don't have the right to change the state." +msgid "You don't have the right to change the pseudo." +msgstr "Vous n'avez pas le droit de changer l'état." + +#: users/models.py:1205 users/models.py:1220 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1212 +#: users/models.py:1235 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1227 +#: users/models.py:1250 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1243 +#: users/models.py:1266 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1268 +#: users/models.py:1291 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1277 +#: users/models.py:1300 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1292 users/models.py:1501 +#: users/models.py:1315 users/models.py:1525 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1309 +#: users/models.py:1332 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1330 +#: users/models.py:1354 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1337 +#: users/models.py:1361 msgid "Email field cannot be empty." msgstr "Le champ mail ne peut pas ^êêtre vide" -#: users/models.py:1344 +#: users/models.py:1368 msgid "You can't use a {} address as an external contact address." msgstr "Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." -#: users/models.py:1371 +#: users/models.py:1395 msgid "member" msgstr "adhérent" -#: users/models.py:1372 +#: users/models.py:1396 msgid "members" msgstr "adhérents" -#: users/models.py:1389 +#: users/models.py:1413 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1414 +#: users/models.py:1438 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1424 +#: users/models.py:1448 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1454 +#: users/models.py:1478 msgid "club" msgstr "club" -#: users/models.py:1455 +#: users/models.py:1479 msgid "clubs" msgstr "clubs" -#: users/models.py:1466 +#: users/models.py:1490 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1474 +#: users/models.py:1498 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1569 +#: users/models.py:1593 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1575 +#: users/models.py:1599 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1576 users/views.py:349 +#: users/models.py:1600 users/views.py:349 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1577 +#: users/models.py:1601 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1581 +#: users/models.py:1605 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1648 +#: users/models.py:1672 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1649 +#: users/models.py:1673 msgid "school" msgstr "établissement" -#: users/models.py:1650 +#: users/models.py:1674 msgid "schools" msgstr "établissements" -#: users/models.py:1669 +#: users/models.py:1693 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1675 +#: users/models.py:1699 msgid "Description." msgstr "Description." -#: users/models.py:1678 +#: users/models.py:1702 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1679 +#: users/models.py:1703 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1680 +#: users/models.py:1704 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1725 +#: users/models.py:1749 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1726 users/views.py:649 +#: users/models.py:1750 users/views.py:649 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1727 +#: users/models.py:1751 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1745 +#: users/models.py:1769 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1746 +#: users/models.py:1770 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1747 +#: users/models.py:1771 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1758 +#: users/models.py:1782 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1759 users/views.py:400 +#: users/models.py:1783 users/views.py:400 msgid "ban" msgstr "bannissement" -#: users/models.py:1760 +#: users/models.py:1784 msgid "bans" msgstr "bannissements" -#: users/models.py:1797 +#: users/models.py:1821 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1845 +#: users/models.py:1869 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1846 +#: users/models.py:1870 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1847 +#: users/models.py:1871 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1867 +#: users/models.py:1891 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2065 +#: users/models.py:2089 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2068 +#: users/models.py:2092 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2073 +#: users/models.py:2097 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2075 +#: users/models.py:2099 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2076 +#: users/models.py:2100 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2104 users/models.py:2139 users/models.py:2173 -#: users/models.py:2207 +#: users/models.py:2128 users/models.py:2163 users/models.py:2197 +#: users/models.py:2231 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2109 +#: users/models.py:2133 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2119 +#: users/models.py:2143 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2145 +#: users/models.py:2169 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2165 +#: users/models.py:2189 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -690,13 +702,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2179 +#: users/models.py:2203 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2199 +#: users/models.py:2223 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -704,13 +716,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2213 +#: users/models.py:2237 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2222 +#: users/models.py:2246 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." From a383a041815520b8e2fdcebd4658f21061c2a12f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 20:26:44 +0200 Subject: [PATCH 207/490] Allow viewing history of deleted elements --- logs/models.py | 52 +++++++++++++++++++++----------------------------- logs/views.py | 32 ++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/logs/models.py b/logs/models.py index e3f0cba8..cc4f7b93 100644 --- a/logs/models.py +++ b/logs/models.py @@ -290,18 +290,19 @@ class History: self._last_version = None self.event_type = HistoryEvent - def get(self, instance): + def get(self, instance_id, model): """ - :param interface: The instance to lookup + :param instance_id: int, The id of the instance to lookup + :param model: class, The type of object to lookup :return: list or None, a list of HistoryEvent, in reverse chronological order """ self.events = [] - # Get all the versions for this interface, with the oldest first + # Get all the versions for this instance, with the oldest first self._last_version = None interface_versions = filter( - lambda x: x.field_dict["id"] == instance.id, - Version.objects.get_for_model(type(instance)).order_by("revision__date_created") + lambda x: x.field_dict["id"] == instance_id, + Version.objects.get_for_model(model).order_by("revision__date_created") ) for version in interface_versions: @@ -345,16 +346,6 @@ class History: class UserHistoryEvent(HistoryEvent): - def __init__(self, user, version, previous_version=None, edited_fields=None): - """ - :param user: User, The user who's history is being built - :param version: Version, the version of the user for this event - :param previous_version: Version, the version of the user before this event - :param edited_fields: list, The list of modified fields by this event - """ - super(UserHistoryEvent, self).__init__(version, previous_version, edited_fields) - self.user = user - def _repr(self, name, value): """ Returns the best representation of the given field @@ -424,7 +415,6 @@ class UserHistoryEvent(HistoryEvent): def __eq__(self, other): return ( - self.user.id == other.user.id and self.edited_fields == other.edited_fields and self.date == other.date and self.performed_by == other.performed_by @@ -432,13 +422,12 @@ class UserHistoryEvent(HistoryEvent): ) def __hash__(self): - return hash((self.user.id, frozenset(self.edited_fields), self.date, self.performed_by, self.comment)) + return hash((frozenset(self.edited_fields), self.date, self.performed_by, self.comment)) def __repr__(self): - return "{} edited fields {} of {} ({})".format( + return "{} edited fields {} ({})".format( self.performed_by, self.edited_fields or "nothing", - self.user, self.comment or "No comment" ) @@ -448,24 +437,24 @@ class UserHistory(History): super(UserHistory, self).__init__() self.event_type = UserHistoryEvent - def get(self, user): + def get(self, user_id): """ - :param user: User, the user to lookup + :param user_id: int, the id of the user to lookup :return: list or None, a list of UserHistoryEvent, in reverse chronological order """ self.events = [] # Find whether this is a Club or an Adherent try: - obj = Adherent.objects.get(user_ptr_id=user.id) + obj = Adherent.objects.get(user_ptr_id=user_id) except Adherent.DoesNotExist: - obj = Club.objects.get(user_ptr_id=user.id) + obj = Club.objects.get(user_ptr_id=user_id) # Add as "related" histories the list of Machine objects # that were once owned by this user self.related = filter( - lambda x: x.field_dict["user_id"] == user.id, - Version.objects.get_for_model(Machine).order_by("-revision__date_created") + lambda x: x.field_dict["user_id"] == user_id, + Version.objects.get_for_model(Machine).order_by("revision__date_created") ) self.related = [RelatedHistory( m.field_dict["name"] or _("None"), @@ -476,7 +465,7 @@ class UserHistory(History): # Get all the versions for this user, with the oldest first self._last_version = None user_versions = filter( - lambda x: x.field_dict["id"] == user.id, + lambda x: x.field_dict["id"] == user_id, Version.objects.get_for_model(User).order_by("revision__date_created") ) @@ -547,12 +536,12 @@ class MachineHistory(History): super(MachineHistory, self).__init__() self.event_type = MachineHistoryEvent - def get(self, machine): + def get(self, machine_id): # Add as "related" histories the list of Interface objects # that were once assigned to this machine self.related = list(filter( - lambda x: x.field_dict["machine_id"] == machine.id, - Version.objects.get_for_model(Interface).order_by("-revision__date_created") + lambda x: x.field_dict["machine_id"] == machine_id, + Version.objects.get_for_model(Interface).order_by("revision__date_created") )) # Create RelatedHistory objects and remove duplicates @@ -562,7 +551,7 @@ class MachineHistory(History): i.field_dict["id"]) for i in self.related] self.related = list(dict.fromkeys(self.related)) - return super(MachineHistory, self).get(machine) + return super(MachineHistory, self).get(machine_id, Machine) class InterfaceHistoryEvent(HistoryEvent): @@ -606,3 +595,6 @@ class InterfaceHistory(History): def __init__(self): super(InterfaceHistory, self).__init__() self.event_type = InterfaceHistoryEvent + + def get(self, interface_id): + return super(InterfaceHistory, self).get(machine_id, Interface) diff --git a/logs/views.py b/logs/views.py index 54432794..c0f3d6e9 100644 --- a/logs/views.py +++ b/logs/views.py @@ -514,22 +514,44 @@ def stats_search_machine_history(request): return render(request, "logs/search_machine_history.html", {"history_form": history_form}) -def get_history_object(request, model, object_name, object_id): +def get_history_object(request, model, object_name, object_id, allow_deleted=False): """Get the objet of type model with the given object_id Handles permissions and DoesNotExist errors """ + instance = None + is_deleted = False + try: object_name_id = object_name + "id" kwargs = {object_name_id: object_id} instance = model.get_instance(**kwargs) except model.DoesNotExist: + pass + + if instance is None and allow_deleted: + # Try to find an instance among the Version objects + is_deleted = True + versions = filter( + lambda x: x.field_dict["id"] == object_id, + Version.objects.get_for_model(model) + ) + versions = list(versions) + if len(versions): + instance = versions[0] + + if instance is None: messages.error(request, _("Nonexistent entry.")) return False, redirect( reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) - can, msg, _permissions = instance.can_view(request.user) - if not can: + if is_deleted: + can_view = can_view_app("logs") + msg = None + else: + can_view, msg, _permissions = instance.can_view(request.user) + + if not can_view: messages.error( request, msg or _("You don't have the right to access this menu.") ) @@ -559,7 +581,7 @@ def detailed_history(request, object_name, object_id): raise Http404(_("No model found.")) # Get instance and check permissions - can_view, instance = get_history_object(request, model, object_name, object_id) + can_view, instance = get_history_object(request, model, object_name, object_id, allow_deleted=True) if not can_view: return instance @@ -567,7 +589,7 @@ def detailed_history(request, object_name, object_id): max_result = GeneralOption.get_cached_value("pagination_number") events = re2o_paginator( request, - history.get(instance), + history.get(object_id), max_result ) From 81693808d5b01fb5605d5b5299fa82cfa3cf7c3a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 20:30:54 +0200 Subject: [PATCH 208/490] Fix typos in logs/models.py --- logs/models.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/logs/models.py b/logs/models.py index cc4f7b93..c52762a2 100644 --- a/logs/models.py +++ b/logs/models.py @@ -415,7 +415,7 @@ class UserHistoryEvent(HistoryEvent): def __eq__(self, other): return ( - and self.edited_fields == other.edited_fields + self.edited_fields == other.edited_fields and self.date == other.date and self.performed_by == other.performed_by and self.comment == other.comment @@ -470,7 +470,7 @@ class UserHistory(History): ) for version in user_versions: - self._add_revision(user, version) + self._add_revision(version) # Do the same thing for the Adherent of Club self._last_version = None @@ -480,7 +480,7 @@ class UserHistory(History): ) for version in obj_versions: - self._add_revision(user, version) + self._add_revision(version) # Remove duplicates and sort self.events = list(dict.fromkeys(self.events)) @@ -490,7 +490,7 @@ class UserHistory(History): reverse=True ) - def _add_revision(self, user, version): + def _add_revision(self, version): """ Add a new revision to the chronological order :param user: User, The user displayed in this history @@ -509,7 +509,7 @@ class UserHistory(History): self._last_version = version return - evt = UserHistoryEvent(user, version, self._last_version, diff) + evt = UserHistoryEvent(version, self._last_version, diff) self.events.append(evt) self._last_version = version @@ -597,4 +597,4 @@ class InterfaceHistory(History): self.event_type = InterfaceHistoryEvent def get(self, interface_id): - return super(InterfaceHistory, self).get(machine_id, Interface) + return super(InterfaceHistory, self).get(interface_id, Interface) From 434ab155431be848e94c571655a2a4f026d721d0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 20:50:31 +0200 Subject: [PATCH 209/490] Better handle deleted objects in detailed history view --- logs/models.py | 29 +++++++++++++++++++++++------ logs/views.py | 21 ++++++++------------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/logs/models.py b/logs/models.py index c52762a2..144bd716 100644 --- a/logs/models.py +++ b/logs/models.py @@ -308,6 +308,10 @@ class History: for version in interface_versions: self._add_revision(version) + # Return None if interface_versions was empty + if self._last_version is None: + return None + return self.events[::-1] def _compute_diff(self, v1, v2, ignoring=[]): @@ -444,13 +448,26 @@ class UserHistory(History): """ self.events = [] - # Find whether this is a Club or an Adherent - try: - obj = Adherent.objects.get(user_ptr_id=user_id) - except Adherent.DoesNotExist: - obj = Club.objects.get(user_ptr_id=user_id) + # Try to find an Adherent object + adherents = filter( + lambda x: x.field_dict["user_ptr_id"] == user_id, + Version.objects.get_for_model(Adherent) + ) + obj = next(adherents, None) - # Add as "related" histories the list of Machine objects + # Fallback on a Club + if obj is None: + clubs = filter( + lambda x: x.field_dict["user_ptr_id"] == user_id, + Version.objects.get_for_model(Club) + ) + obj = next(clubs, None) + + # If nothing was found, abort + if obj is None: + return None + + # Add in "related" elements the list of Machine objects # that were once owned by this user self.related = filter( lambda x: x.field_dict["user_id"] == user_id, diff --git a/logs/views.py b/logs/views.py index c0f3d6e9..981d3d00 100644 --- a/logs/views.py +++ b/logs/views.py @@ -518,7 +518,6 @@ def get_history_object(request, model, object_name, object_id, allow_deleted=Fal """Get the objet of type model with the given object_id Handles permissions and DoesNotExist errors """ - instance = None is_deleted = False try: @@ -526,20 +525,9 @@ def get_history_object(request, model, object_name, object_id, allow_deleted=Fal kwargs = {object_name_id: object_id} instance = model.get_instance(**kwargs) except model.DoesNotExist: - pass - - if instance is None and allow_deleted: - # Try to find an instance among the Version objects is_deleted = True - versions = filter( - lambda x: x.field_dict["id"] == object_id, - Version.objects.get_for_model(model) - ) - versions = list(versions) - if len(versions): - instance = versions[0] - if instance is None: + if is_deleted and not allow_deleted: messages.error(request, _("Nonexistent entry.")) return False, redirect( reverse("users:profil", kwargs={"userid": str(request.user.id)}) @@ -593,6 +581,13 @@ def detailed_history(request, object_name, object_id): max_result ) + # Events is None if object wasn't found + if events is None: + messages.error(request, _("Nonexistent entry.")) + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + return render( request, "logs/detailed_history.html", From 4b96dc0a214e94be178d4d4a96b3ef5dc9ae0012 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 19:20:55 +0000 Subject: [PATCH 210/490] Handle history for non-existant objects --- logs/models.py | 6 ++++-- logs/views.py | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/logs/models.py b/logs/models.py index 144bd716..57597c5b 100644 --- a/logs/models.py +++ b/logs/models.py @@ -454,6 +454,7 @@ class UserHistory(History): Version.objects.get_for_model(Adherent) ) obj = next(adherents, None) + model = Adherent # Fallback on a Club if obj is None: @@ -462,6 +463,7 @@ class UserHistory(History): Version.objects.get_for_model(Club) ) obj = next(clubs, None) + model = Club # If nothing was found, abort if obj is None: @@ -492,8 +494,8 @@ class UserHistory(History): # Do the same thing for the Adherent of Club self._last_version = None obj_versions = filter( - lambda x: x.field_dict["id"] == obj.id, - Version.objects.get_for_model(type(obj)).order_by("revision__date_created") + lambda x: x.field_dict["id"] == user_id, + Version.objects.get_for_model(model).order_by("revision__date_created") ) for version in obj_versions: diff --git a/logs/views.py b/logs/views.py index 981d3d00..007da9f7 100644 --- a/logs/views.py +++ b/logs/views.py @@ -526,6 +526,7 @@ def get_history_object(request, model, object_name, object_id, allow_deleted=Fal instance = model.get_instance(**kwargs) except model.DoesNotExist: is_deleted = True + instance = None if is_deleted and not allow_deleted: messages.error(request, _("Nonexistent entry.")) @@ -575,11 +576,7 @@ def detailed_history(request, object_name, object_id): # Generate the pagination with the objects max_result = GeneralOption.get_cached_value("pagination_number") - events = re2o_paginator( - request, - history.get(object_id), - max_result - ) + events = history.get(int(object_id)) # Events is None if object wasn't found if events is None: @@ -588,6 +585,9 @@ def detailed_history(request, object_name, object_id): reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) + # Add the paginator in case there are many results + events = re2o_paginator(request, events, max_result) + return render( request, "logs/detailed_history.html", From a062536f80718dd3e207b76fd8b4f888f4ad4467 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 21:25:00 +0200 Subject: [PATCH 211/490] Add title to history of deleted objects --- logs/models.py | 18 ++++++++++++++++-- logs/templates/logs/detailed_history.html | 2 +- logs/views.py | 5 ++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/logs/models.py b/logs/models.py index 57597c5b..c41db6b7 100644 --- a/logs/models.py +++ b/logs/models.py @@ -285,6 +285,7 @@ class HistoryEvent: class History: def __init__(self): + self.name = None self.events = [] self.related = [] # For example, a machine has a list of its interfaces self._last_version = None @@ -491,6 +492,9 @@ class UserHistory(History): for version in user_versions: self._add_revision(version) + # Update name + self.name = self._last_version.field_dict["pseudo"] + # Do the same thing for the Adherent of Club self._last_version = None obj_versions = filter( @@ -570,7 +574,12 @@ class MachineHistory(History): i.field_dict["id"]) for i in self.related] self.related = list(dict.fromkeys(self.related)) - return super(MachineHistory, self).get(machine_id, Machine) + events = super(MachineHistory, self).get(machine_id, Machine) + + # Update name + self.name = self._last_version.field_dict["name"] + + return events class InterfaceHistoryEvent(HistoryEvent): @@ -616,4 +625,9 @@ class InterfaceHistory(History): self.event_type = InterfaceHistoryEvent def get(self, interface_id): - return super(InterfaceHistory, self).get(interface_id, Interface) + events = super(InterfaceHistory, self).get(interface_id, Interface) + + # Update name + self.name = self._last_version.field_dict["mac_address"] + + return events diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index 26bae90c..7aaf67a0 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "History" %}{% endblock %} {% block content %} -

    {% blocktrans %}History of {{ object }}{% endblocktrans %}

    +

    {% blocktrans %}History of {{ title }}{% endblocktrans %}

    {% if events %} diff --git a/logs/views.py b/logs/views.py index 007da9f7..ad78bd23 100644 --- a/logs/views.py +++ b/logs/views.py @@ -588,10 +588,13 @@ def detailed_history(request, object_name, object_id): # Add the paginator in case there are many results events = re2o_paginator(request, events, max_result) + # Add a title in case the object was deleted + title = instance or "{} ({})".format(history.name, _("Deleted")) + return render( request, "logs/detailed_history.html", - {"object": instance, "events": events, "related_history": history.related}, + {"title": title, "events": events, "related_history": history.related}, ) From ec6d73869d72ef73d5d06e60bf313c0c28d020a6 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 23 Apr 2020 19:28:15 +0000 Subject: [PATCH 212/490] Add missing translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 25 +++++++++++---------- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 2 +- 12 files changed, 24 insertions(+), 23 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 6b5b3c75..51857377 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 674a417c..782c72b7 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index e9310b54..039ba799 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -58,23 +58,24 @@ msgstr "Date de début" msgid "End date" msgstr "Date de fin" -#: logs/models.py:260 logs/models.py:368 logs/models.py:401 logs/models.py:471 -#: logs/models.py:560 logs/models.py:593 +#: logs/models.py:260 logs/models.py:364 logs/models.py:397 logs/models.py:480 +#: logs/models.py:572 logs/models.py:610 msgid "None" msgstr "Aucun(e)" -#: logs/models.py:378 logs/models.py:397 logs/models.py:411 logs/models.py:540 -#: logs/models.py:580 logs/models.py:585 logs/models.py:590 logs/models.py:600 +#: logs/models.py:374 logs/models.py:393 logs/models.py:407 logs/models.py:552 +#: logs/models.py:597 logs/models.py:602 logs/models.py:607 logs/models.py:617 +#: logs/views.py:592 msgid "Deleted" msgstr "Supprimé(e)" -#: logs/models.py:385 logs/models.py:390 +#: logs/models.py:381 logs/models.py:386 #: logs/templates/logs/detailed_history.html:52 #: logs/templates/logs/machine_history.html:55 msgid "Unknown" msgstr "Inconnu(e)" -#: logs/models.py:588 +#: logs/models.py:605 msgid "No name" msgstr "Sans nom" @@ -204,8 +205,8 @@ msgid "History" msgstr "Historique" #: logs/templates/logs/detailed_history.html:31 -msgid "History of %(object)s" -msgstr "Historique de %(object)s" +msgid "History of %(title)s" +msgstr "Historique de %(title)s" #: logs/templates/logs/detailed_history.html:38 msgid "Performed by" @@ -420,14 +421,14 @@ msgstr "droits" msgid "actions" msgstr "actions" -#: logs/views.py:526 +#: logs/views.py:532 logs/views.py:583 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:534 +#: logs/views.py:545 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." -#: logs/views.py:559 logs/views.py:606 +#: logs/views.py:570 logs/views.py:626 msgid "No model found." msgstr "Aucun modèle trouvé." diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 81109ef4..e8abd822 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 026a1da2..3439441d 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 00ea2a38..8423e0cb 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 625eda15..7bba1fe6 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index e33d68fd..c69e0148 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 7187c189..5e7b31d0 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index bb56a162..42c96960 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index ee5bfb93..5bba5b46 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index f5029a64..6b57f34b 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 20:09+0200\n" +"POT-Creation-Date: 2020-04-23 21:25+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" From fc21a1da3e2f7c9c68c84b952703f23bac31b4e7 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 15:37:05 +0200 Subject: [PATCH 213/490] Add view to filter event logs --- logs/forms.py | 55 +++++++++++++++++++++- logs/models.py | 36 +++++++++++++- logs/templates/logs/search_stats_logs.html | 46 ++++++++++++++++++ logs/views.py | 37 +++++++++------ 4 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 logs/templates/logs/search_stats_logs.html diff --git a/logs/forms.py b/logs/forms.py index 07e421c9..1ebfdcd4 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -25,15 +25,68 @@ from django.forms import Form from django.utils.translation import ugettext_lazy as _ from re2o.base import get_input_formats_help_text +import inspect + +# Import all models in which there are classes to be filtered on +import machines.models +import preferences.models +import tickets.models +import topologie.models +import users.models + + +def get_classes(module): + classes = [] + + for name, obj in inspect.getmembers(module): + if inspect.isclass(obj): + classes.append((obj, name)) + + return classes + + +# Get the list of all imported classes +modules = [machines.models, preferences.models, tickets.models, topologie.models, users.models] +classes = sum([get_classes(m) for m in modules]) + +CHOICES_ACTION_TYPE = classes + CHOICES_TYPE = ( ("ip", _("IPv4")), ("mac", _("MAC address")), ) +class ActionsSearchForm(Form): + """The form for a simple search""" + u = forms.CharField( + label=_("Performed by"), + max_length=100, + required=False, + queryset=users.models.User.objects.all() + ) + t = forms.MultipleChoiceField( + label=_("Action type"), + required=False, + widget=forms.CheckboxSelectMultiple, + choices=CHOICES_ACTION_TYPE, + initial=[i[0] for i in CHOICES_ACTION_TYPE], + ) + s = forms.DateField(required=False, label=_("Start date")) + e = forms.DateField(required=False, label=_("End date")) + + def __init__(self, *args, **kwargs): + super(MachineHistorySearchForm, self).__init__(*args, **kwargs) + self.fields["s"].help_text = get_input_formats_help_text( + self.fields["s"].input_formats + ) + self.fields["e"].help_text = get_input_formats_help_text( + self.fields["e"].input_formats + ) + + class MachineHistorySearchForm(Form): """The form for a simple search""" - q = forms.CharField( label=_("Search"), max_length=100, diff --git a/logs/models.py b/logs/models.py index c41db6b7..4773d9b5 100644 --- a/logs/models.py +++ b/logs/models.py @@ -21,9 +21,10 @@ """logs.models The models definitions for the logs app """ -from reversion.models import Version +from reversion.models import Version, Revision from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import Group +from django.db.models import Q from machines.models import IpList from machines.models import Interface @@ -36,6 +37,39 @@ from topologie.models import Room from topologie.models import Port +class ActionsSearch: + def get(self, params): + """ + :param params: dict built by the search view + :return: QuerySet of Revision objects + """ + user = params.get("u", None) + start = params.get("s", None) + end = params.get("e", None) + actions_type = params.get("t", None) + + query = Q() + + if user: + query &= Q(user=user) + + if start: + query &= Q(date_created__geq=start) + + if end: + query &= Q(date_created__leq=end) + + if actions_type: + query &= Q(version_set__object__in=actions_type) + + return ( + Revision.objects.all() + .filter(query) + .select_related("user") + .prefetch_related("version_set__object") + ) + + class MachineHistorySearchEvent: def __init__(self, user, machine, interface, start=None, end=None): """ diff --git a/logs/templates/logs/search_stats_logs.html b/logs/templates/logs/search_stats_logs.html new file mode 100644 index 00000000..d0b56ea6 --- /dev/null +++ b/logs/templates/logs/search_stats_logs.html @@ -0,0 +1,46 @@ +{% extends 'logs/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 © 2020 Jean-Romain Garnier + +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 "Search events" %}{% endblock %} + +{% block content %} + +
    +

    {% trans "Search events" %}

    + + {% bootstrap_field actions_form.u %} + {% bootstrap_field actions_form.t %} + {% bootstrap_field actions_form.s %} + {% bootstrap_field actions_form.e %} + {% trans "Search" as tr_search %} + {% bootstrap_button tr_search button_type="submit" icon="search" %} + +
    +
    +
    +
    +
    +{% endblock %} diff --git a/logs/views.py b/logs/views.py index ad78bd23..6075d928 100644 --- a/logs/views.py +++ b/logs/views.py @@ -102,13 +102,17 @@ from re2o.base import re2o_paginator, SortTable from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view from .models import ( + ActionsSearch, MachineHistorySearch, UserHistory, MachineHistory, InterfaceHistory ) -from .forms import MachineHistorySearchForm +from .forms import ( + ActionsSearchForm, + MachineHistorySearchForm +) @login_required @@ -158,20 +162,23 @@ def index(request): def stats_logs(request): """Affiche l'ensemble des logs et des modifications sur les objets, classés par date croissante, en vrac""" - pagination_number = GeneralOption.get_cached_value("pagination_number") - revisions = ( - Revision.objects.all() - .select_related("user") - .prefetch_related("version_set__object") - ) - revisions = SortTable.sort( - revisions, - request.GET.get("col"), - request.GET.get("order"), - SortTable.LOGS_STATS_LOGS, - ) - revisions = re2o_paginator(request, revisions, pagination_number) - return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) + actions_form = ActionsSearchForm(request.GET or None) + + if actions_form.is_valid(): + actions = ActionsSearch() + revisions = actions.get(actions_form.cleaned_data) + revisions = SortTable.sort( + revisions, + request.GET.get("col"), + request.GET.get("order"), + SortTable.LOGS_STATS_LOGS, + ) + + pagination_number = GeneralOption.get_cached_value("pagination_number") + revisions = re2o_paginator(request, revisions, pagination_number) + return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) + + return render(request, "logs/search_stats_logs.html", {"actions_form": actions_form}) @login_required From f196bc8046bd57d8acace5368a6ccb6b970440f2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 13:56:02 +0000 Subject: [PATCH 214/490] Fix actions filtering --- logs/forms.py | 11 ++++++----- logs/models.py | 2 +- logs/templates/logs/search_stats_logs.html | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index 1ebfdcd4..72212b0c 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -40,16 +40,18 @@ def get_classes(module): for name, obj in inspect.getmembers(module): if inspect.isclass(obj): - classes.append((obj, name)) + classes.append(name) return classes # Get the list of all imported classes modules = [machines.models, preferences.models, tickets.models, topologie.models, users.models] -classes = sum([get_classes(m) for m in modules]) +classes = sum([get_classes(m) for m in modules], []) -CHOICES_ACTION_TYPE = classes +CHOICES_ACTION_TYPE = [ + (str(i), classes[i]) for i in range(len(classes)) +] CHOICES_TYPE = ( ("ip", _("IPv4")), @@ -63,7 +65,6 @@ class ActionsSearchForm(Form): label=_("Performed by"), max_length=100, required=False, - queryset=users.models.User.objects.all() ) t = forms.MultipleChoiceField( label=_("Action type"), @@ -76,7 +77,7 @@ class ActionsSearchForm(Form): e = forms.DateField(required=False, label=_("End date")) def __init__(self, *args, **kwargs): - super(MachineHistorySearchForm, self).__init__(*args, **kwargs) + super(ActionsSearchForm, self).__init__(*args, **kwargs) self.fields["s"].help_text = get_input_formats_help_text( self.fields["s"].input_formats ) diff --git a/logs/models.py b/logs/models.py index 4773d9b5..f1db0c08 100644 --- a/logs/models.py +++ b/logs/models.py @@ -60,7 +60,7 @@ class ActionsSearch: query &= Q(date_created__leq=end) if actions_type: - query &= Q(version_set__object__in=actions_type) + query &= Q(version__content_type__in=actions_type) return ( Revision.objects.all() diff --git a/logs/templates/logs/search_stats_logs.html b/logs/templates/logs/search_stats_logs.html index d0b56ea6..5faa8066 100644 --- a/logs/templates/logs/search_stats_logs.html +++ b/logs/templates/logs/search_stats_logs.html @@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Search events" %}

    {% bootstrap_field actions_form.u %} - {% bootstrap_field actions_form.t %} + {% include 'buttons/multiple_checkbox_alt.html' with field=actions_form.t %} {% bootstrap_field actions_form.s %} {% bootstrap_field actions_form.e %} {% trans "Search" as tr_search %} From d70632c624eacb4990d967e368aa7962721426e1 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 16:17:20 +0200 Subject: [PATCH 215/490] Make event logs filter more user friendly --- logs/forms.py | 58 ++++++++++++++++++++++++++++++++++++++++---------- logs/models.py | 24 ++++++++++++++++++--- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index 72212b0c..ffae8e52 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -28,6 +28,7 @@ from re2o.base import get_input_formats_help_text import inspect # Import all models in which there are classes to be filtered on +import cotisations.models import machines.models import preferences.models import tickets.models @@ -35,7 +36,23 @@ import topologie.models import users.models -def get_classes(module): +CHOICES_ACTION_TYPE = ( + ("users", _("Users")), + ("machines", _("Machines")), + ("subscriptions", _("Subscription")), + ("whitelists", _("Whitelists")), + ("bans", _("Bans")), + ("topology", _("Topology")), + ("all", _("All")), +) + +CHOICES_TYPE = ( + ("ip", _("IPv4")), + ("mac", _("MAC address")), +) + + +def all_classes(module): classes = [] for name, obj in inspect.getmembers(module): @@ -45,18 +62,37 @@ def get_classes(module): return classes -# Get the list of all imported classes -modules = [machines.models, preferences.models, tickets.models, topologie.models, users.models] -classes = sum([get_classes(m) for m in modules], []) +def classes_for_action_type(action_type): + """Return the list of class names to be displayed for a + given actions type filter""" + if action_type == "users": + return [ + users.models.User.__name__, + users.models.Adherent.__name__, + users.models.Club.__name__, + users.models.EMailAddress.__name__ + ] -CHOICES_ACTION_TYPE = [ - (str(i), classes[i]) for i in range(len(classes)) -] + if action_type == "machines": + return [ + machines.models.Machine.__name__, + machines.models.Interface.__name__ + ] -CHOICES_TYPE = ( - ("ip", _("IPv4")), - ("mac", _("MAC address")), -) + if action_type == "subscriptions": + return all_classes(cotisations.models) + + if action_type == "whitelists": + return [users.models.Whitelist.__name__] + + if action_type == "ban": + return [users.models.Ban.__name__] + + if action_type == "topology": + return all_classes(topologie.models) + + # "all" is a special case, just return None + return None class ActionsSearchForm(Form): diff --git a/logs/models.py b/logs/models.py index f1db0c08..5ef9b0ce 100644 --- a/logs/models.py +++ b/logs/models.py @@ -36,6 +36,7 @@ from users.models import Club from topologie.models import Room from topologie.models import Port +from .forms import classes_for_action_type class ActionsSearch: def get(self, params): @@ -46,7 +47,7 @@ class ActionsSearch: user = params.get("u", None) start = params.get("s", None) end = params.get("e", None) - actions_type = params.get("t", None) + action_types = params.get("t", None) query = Q() @@ -59,8 +60,9 @@ class ActionsSearch: if end: query &= Q(date_created__leq=end) - if actions_type: - query &= Q(version__content_type__in=actions_type) + action_classes = self.classes_for_action_types(action_types) + if action_classes: + query &= Q(object__classname=action_classes) return ( Revision.objects.all() @@ -69,6 +71,22 @@ class ActionsSearch: .prefetch_related("version_set__object") ) + def classes_for_action_types(self, action_types): + if action_types is None: + return None + + classes = [] + for action_type in action_types: + c = classes_for_action_type(action_type) + + # Selecting "all" removes the filter + if c is None: + return None + + classes += c + + return classes + class MachineHistorySearchEvent: def __init__(self, user, machine, interface, start=None, end=None): From 43bfed664d6d34ba2a8682a15754bebed2382d04 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 14:34:36 +0000 Subject: [PATCH 216/490] Fix filtering action logs by type --- logs/forms.py | 2 +- logs/models.py | 8 ++++---- logs/templates/logs/aff_stats_logs.html | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index ffae8e52..ae281e98 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -85,7 +85,7 @@ def classes_for_action_type(action_type): if action_type == "whitelists": return [users.models.Whitelist.__name__] - if action_type == "ban": + if action_type == "bans": return [users.models.Ban.__name__] if action_type == "topology": diff --git a/logs/models.py b/logs/models.py index 5ef9b0ce..c24ec525 100644 --- a/logs/models.py +++ b/logs/models.py @@ -60,9 +60,9 @@ class ActionsSearch: if end: query &= Q(date_created__leq=end) - action_classes = self.classes_for_action_types(action_types) - if action_classes: - query &= Q(object__classname=action_classes) + action_models = self.models_for_action_types(action_types) + if action_models: + query &= Q(version__content_type__model__in=action_models) return ( Revision.objects.all() @@ -71,7 +71,7 @@ class ActionsSearch: .prefetch_related("version_set__object") ) - def classes_for_action_types(self, action_types): + def models_for_action_types(self, action_types): if action_types is None: return None diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index adccc95f..ab12a02f 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -47,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for reversion in revision.version_set.all %}
    - + From bd187b9f2cacffa28ff9d14c550a57a212f2b3a8 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 16:43:11 +0200 Subject: [PATCH 217/490] Fix date filter in event logs --- logs/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/logs/models.py b/logs/models.py index c24ec525..e6a2636c 100644 --- a/logs/models.py +++ b/logs/models.py @@ -38,6 +38,7 @@ from topologie.models import Port from .forms import classes_for_action_type + class ActionsSearch: def get(self, params): """ @@ -55,10 +56,10 @@ class ActionsSearch: query &= Q(user=user) if start: - query &= Q(date_created__geq=start) + query &= Q(date_created__gte=start) if end: - query &= Q(date_created__leq=end) + query &= Q(date_created__lte=end) action_models = self.models_for_action_types(action_types) if action_models: From 6eb8ab637fb1d944a525fc77724fdd6d7deb5b60 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 16:50:57 +0200 Subject: [PATCH 218/490] Fix user filter in event logs --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index e6a2636c..b5d7d080 100644 --- a/logs/models.py +++ b/logs/models.py @@ -53,7 +53,7 @@ class ActionsSearch: query = Q() if user: - query &= Q(user=user) + query &= Q(user__pseudo=user) if start: query &= Q(date_created__gte=start) From e54026c40a56652554c9d7ed12f1a8714c452a2a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 17:35:48 +0200 Subject: [PATCH 219/490] Rework event logs view --- logs/models.py | 32 ++++++++++++++++++++++++- logs/templates/logs/aff_stats_logs.html | 16 +++++++++---- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/logs/models.py b/logs/models.py index b5d7d080..4aa0bcc4 100644 --- a/logs/models.py +++ b/logs/models.py @@ -25,6 +25,7 @@ from reversion.models import Version, Revision from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import Group from django.db.models import Q +from django.apps import apps from machines.models import IpList from machines.models import Interface @@ -39,6 +40,34 @@ from topologie.models import Port from .forms import classes_for_action_type +class VersionAction: + def __init__(self, version): + self.version = version + + def name(self): + return self.version.object_repr + + def application(self): + return self.version.content_type.app_label + + def model_name(self): + return self.version.content_type.model + + def object_id(self): + return self.version.object_id + + def object_type(self): + return apps.get_model(self.application(), self.model_name()) + + +class RevisionAction: + """A Revision may group multiple Version objects together""" + def __init__(self, revision): + self.performed_by = revision.user + self.revision = revision + self.versions = [VersionAction(v) for v in revision.version_set.all()] + + class ActionsSearch: def get(self, params): """ @@ -65,7 +94,8 @@ class ActionsSearch: if action_models: query &= Q(version__content_type__model__in=action_models) - return ( + return map( + RevisionAction, Revision.objects.all() .filter(query) .select_related("user") diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index ab12a02f..c161cdbd 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -34,7 +34,6 @@ with this program; if not, write to the Free Software Foundation, Inc., - {% trans "Edited by" as tr_edited_by %} {% trans "Date of editing" as tr_date_of_editing %} @@ -44,11 +43,18 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for revision in revisions_list %} - {% for reversion in revision.version_set.all %} + {% for version in revision.versions %} - - - + + {% can_edit_history %} From ed086ae255b5ee2d7d1c984ffd1d7e1889bb6cb3 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 17:47:09 +0200 Subject: [PATCH 220/490] Fix links in event logs view --- logs/models.py | 12 ++++++++++-- logs/templates/logs/aff_stats_logs.html | 6 +++--- logs/views.py | 2 ++ 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/logs/models.py b/logs/models.py index 4aa0bcc4..ec86d415 100644 --- a/logs/models.py +++ b/logs/models.py @@ -67,6 +67,15 @@ class RevisionAction: self.revision = revision self.versions = [VersionAction(v) for v in revision.version_set.all()] + def id(self): + return self.revision.id + + def date_created(self): + return self.revision.date_created + + def comment(self): + return self.revision.get_comment() + class ActionsSearch: def get(self, params): @@ -94,8 +103,7 @@ class ActionsSearch: if action_models: query &= Q(version__content_type__model__in=action_models) - return map( - RevisionAction, + return ( Revision.objects.all() .filter(query) .select_related("user") diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index c161cdbd..9fb6a496 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -46,13 +46,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for version in revision.versions %} diff --git a/logs/views.py b/logs/views.py index 6075d928..26cab9c5 100644 --- a/logs/views.py +++ b/logs/views.py @@ -103,6 +103,7 @@ from re2o.acl import can_view_all, can_view_app, can_edit_history, can_view from .models import ( ActionsSearch, + RevisionAction, MachineHistorySearch, UserHistory, MachineHistory, @@ -176,6 +177,7 @@ def stats_logs(request): pagination_number = GeneralOption.get_cached_value("pagination_number") revisions = re2o_paginator(request, revisions, pagination_number) + revisions = map(RevisionAction, revisions) return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) return render(request, "logs/search_stats_logs.html", {"actions_form": actions_form}) From 3c61a4cd041c69cfdf939686da3f80bf1b4762c5 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 17:48:48 +0200 Subject: [PATCH 221/490] Handle empty objects in event logs view --- logs/templates/logs/aff_stats_logs.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index 9fb6a496..a6dd613f 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -46,14 +46,22 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for version in revision.versions %} From 022e9aca93f0def54b8ae39f292f0d7a51854765 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 18:15:53 +0200 Subject: [PATCH 222/490] Attempt to add diff view in event logs --- logs/models.py | 109 ++++++++++++++++-------- logs/templates/logs/aff_stats_logs.html | 15 ++++ 2 files changed, 87 insertions(+), 37 deletions(-) diff --git a/logs/models.py b/logs/models.py index ec86d415..282d60bc 100644 --- a/logs/models.py +++ b/logs/models.py @@ -40,43 +40,6 @@ from topologie.models import Port from .forms import classes_for_action_type -class VersionAction: - def __init__(self, version): - self.version = version - - def name(self): - return self.version.object_repr - - def application(self): - return self.version.content_type.app_label - - def model_name(self): - return self.version.content_type.model - - def object_id(self): - return self.version.object_id - - def object_type(self): - return apps.get_model(self.application(), self.model_name()) - - -class RevisionAction: - """A Revision may group multiple Version objects together""" - def __init__(self, revision): - self.performed_by = revision.user - self.revision = revision - self.versions = [VersionAction(v) for v in revision.version_set.all()] - - def id(self): - return self.revision.id - - def date_created(self): - return self.revision.date_created - - def comment(self): - return self.revision.get_comment() - - class ActionsSearch: def get(self, params): """ @@ -441,6 +404,78 @@ class History: self._last_version = version +class VersionAction(HistoryEvent): + def __init__(self, version): + self.version = version + + def is_useful(self): + # Some versions are duplicates, and don't have a reference + # to any object, so ignore them + return self.version.object_id is not None + + def name(self): + return "{} {}".format(self.model_name().title(), self.version.object_repr) + + def application(self): + return self.version.content_type.app_label + + def model_name(self): + return self.version.content_type.model + + def object_id(self): + return self.version.object_id + + def object_type(self): + return apps.get_model(self.application(), self.model_name()) + + def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]): + self.previous_version = self._previous_version() + self.edited_fields = self._compute_diff(self.version, self.previous_version) + return super(VersionAction, self).edits(hide) + + def _previous_version(self): + model = self.object_type() + return next( + filter( + lambda x: x.field_dict["id"] == self.object_id() and x.revision.date_created < self.version.revision.date_created, + Version.objects.get_for_model(model).order_by("-revision__date_created") + ) + ) + + def _compute_diff(self, v1, v2): + """ + Find the edited field between two versions + :param v1: Version + :param v2: Version + :param ignoring: List, a list of fields to ignore + :return: List of field names + """ + fields = [] + + for key in v1.field_dict.keys(): + if v1.field_dict[key] != v2.field_dict[key]: + fields.append(key) + + return fields + + +class RevisionAction: + """A Revision may group multiple Version objects together""" + def __init__(self, revision): + self.performed_by = revision.user + self.revision = revision + self.versions = [VersionAction(v) for v in revision.version_set.all() if v.is_useful()] + + def id(self): + return self.revision.id + + def date_created(self): + return self.revision.date_created + + def comment(self): + return self.revision.get_comment() + + class UserHistoryEvent(HistoryEvent): def _repr(self, name, value): """ diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index a6dd613f..ffb296ca 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Date of editing" as tr_date_of_editing %} + @@ -64,6 +65,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} + {% can_edit_history %} diff --git a/logs/views.py b/logs/views.py index 26cab9c5..b71187aa 100644 --- a/logs/views.py +++ b/logs/views.py @@ -177,7 +177,10 @@ def stats_logs(request): pagination_number = GeneralOption.get_cached_value("pagination_number") revisions = re2o_paginator(request, revisions, pagination_number) - revisions = map(RevisionAction, revisions) + + # Only do this now so it's not applied to objects which aren't displayed + # It can take a bit of time because it has to compute the diff of each version + revisions.object_list = [RevisionAction(r) for r in revisions.object_list] return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) return render(request, "logs/search_stats_logs.html", {"actions_form": actions_form}) From e2660d0bee478d9680ad5828c77fd990922e6d55 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 16:50:50 +0000 Subject: [PATCH 226/490] Add missing translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 194 ++++++++++++-------- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- users/locale/fr/LC_MESSAGES/django.po | 2 +- 12 files changed, 125 insertions(+), 91 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 51857377..34694672 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 782c72b7..2a420c4c 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language: fr_FR\n" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 039ba799..0ca22d28 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,48 +34,87 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: logs/forms.py:29 logs/templates/logs/machine_history.html:35 +#: logs/forms.py:40 logs/templates/logs/sidebar.html:53 +msgid "Users" +msgstr "Utilisateurs" + +#: logs/forms.py:41 +msgid "Machines" +msgstr "Machines" + +#: logs/forms.py:42 +msgid "Subscription" +msgstr "Cotisations" + +#: logs/forms.py:43 +msgid "Whitelists" +msgstr "Accès gracieux" + +#: logs/forms.py:44 +msgid "Bans" +msgstr "Bannissements" + +#: logs/forms.py:45 logs/views.py:424 +msgid "Topology" +msgstr "Topologie" + +#: logs/forms.py:46 +msgid "All" +msgstr "Tous" + +#: logs/forms.py:50 logs/templates/logs/machine_history.html:35 msgid "IPv4" msgstr "IPv4" -#: logs/forms.py:30 logs/templates/logs/machine_history.html:36 +#: logs/forms.py:51 logs/templates/logs/machine_history.html:36 msgid "MAC address" msgstr "Adresse MAC" -#: logs/forms.py:38 logs/templates/logs/search_machine_history.html:38 -msgid "Search" -msgstr "Rechercher" +#: logs/forms.py:101 logs/templates/logs/detailed_history.html:38 +msgid "Performed by" +msgstr "Effectué(e) par" -#: logs/forms.py:42 -msgid "Search type" -msgstr "Type de recherche" +#: logs/forms.py:106 +msgid "Action type" +msgstr "Type d'action" -#: logs/forms.py:45 logs/templates/logs/machine_history.html:37 +#: logs/forms.py:112 logs/forms.py:135 +#: logs/templates/logs/machine_history.html:37 msgid "Start date" msgstr "Date de début" -#: logs/forms.py:46 logs/templates/logs/machine_history.html:38 +#: logs/forms.py:113 logs/forms.py:136 +#: logs/templates/logs/machine_history.html:38 msgid "End date" msgstr "Date de fin" -#: logs/models.py:260 logs/models.py:364 logs/models.py:397 logs/models.py:480 -#: logs/models.py:572 logs/models.py:610 +#: logs/forms.py:128 logs/templates/logs/search_machine_history.html:38 +#: logs/templates/logs/search_stats_logs.html:38 +msgid "Search" +msgstr "Rechercher" + +#: logs/forms.py:132 +msgid "Search type" +msgstr "Type de recherche" + +#: logs/models.py:314 logs/models.py:498 logs/models.py:531 logs/models.py:614 +#: logs/models.py:706 logs/models.py:744 msgid "None" msgstr "Aucun(e)" -#: logs/models.py:374 logs/models.py:393 logs/models.py:407 logs/models.py:552 -#: logs/models.py:597 logs/models.py:602 logs/models.py:607 logs/models.py:617 -#: logs/views.py:592 +#: logs/models.py:508 logs/models.py:527 logs/models.py:541 logs/models.py:686 +#: logs/models.py:731 logs/models.py:736 logs/models.py:741 logs/models.py:751 +#: logs/views.py:604 msgid "Deleted" msgstr "Supprimé(e)" -#: logs/models.py:381 logs/models.py:386 +#: logs/models.py:515 logs/models.py:520 #: logs/templates/logs/detailed_history.html:52 #: logs/templates/logs/machine_history.html:55 msgid "Unknown" msgstr "Inconnu(e)" -#: logs/models.py:605 +#: logs/models.py:739 msgid "No name" msgstr "Sans nom" @@ -84,25 +123,31 @@ msgid "Edited object" msgstr "Objet modifié" #: logs/templates/logs/aff_stats_logs.html:37 -#: logs/templates/logs/aff_stats_models.html:32 -msgid "Object type" -msgstr "Type d'objet" - -#: logs/templates/logs/aff_stats_logs.html:38 msgid "Edited by" msgstr "Modifié par" -#: logs/templates/logs/aff_stats_logs.html:40 +#: logs/templates/logs/aff_stats_logs.html:39 msgid "Date of editing" msgstr "Date de modification" +#: logs/templates/logs/aff_stats_logs.html:41 +#: logs/templates/logs/detailed_history.html:39 +msgid "Edited" +msgstr "Modifié" + #: logs/templates/logs/aff_stats_logs.html:42 #: logs/templates/logs/detailed_history.html:40 #: logs/templates/logs/machine_history.html:39 msgid "Comment" msgstr "Commentaire" -#: logs/templates/logs/aff_stats_logs.html:58 +#: logs/templates/logs/aff_stats_logs.html:51 +#: logs/templates/logs/detailed_history.html:28 +#: logs/templates/logs/detailed_history.html:85 +msgid "History" +msgstr "Historique" + +#: logs/templates/logs/aff_stats_logs.html:87 #: logs/templates/logs/aff_summary.html:62 #: logs/templates/logs/aff_summary.html:85 #: logs/templates/logs/aff_summary.html:104 @@ -116,6 +161,10 @@ msgstr "Annuler" msgid "Statistics of the set %(key)s" msgstr "Statistiques de l'ensemble %(key)s" +#: logs/templates/logs/aff_stats_models.html:32 +msgid "Object type" +msgstr "Type d'objet" + #: logs/templates/logs/aff_stats_models.html:33 msgid "Number of stored entries" msgstr "Nombre d'entrées enregistrées" @@ -199,23 +248,11 @@ msgstr "" msgid "Confirm" msgstr "Confirmer" -#: logs/templates/logs/detailed_history.html:28 -#: logs/templates/logs/detailed_history.html:85 -msgid "History" -msgstr "Historique" - #: logs/templates/logs/detailed_history.html:31 +#, python-format msgid "History of %(title)s" msgstr "Historique de %(title)s" -#: logs/templates/logs/detailed_history.html:38 -msgid "Performed by" -msgstr "Effectué(e) par" - -#: logs/templates/logs/detailed_history.html:39 -msgid "Edited" -msgstr "Modifié" - #: logs/templates/logs/detailed_history.html:75 msgid "No event" msgstr "Aucun évènement" @@ -232,7 +269,7 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:427 +#: logs/views.py:439 msgid "Actions performed" msgstr "Actions effectuées" @@ -257,6 +294,11 @@ msgstr "Aucun résultat" msgid "Search machine history" msgstr "Rechercher l'historique des machines" +#: logs/templates/logs/search_stats_logs.html:27 +#: logs/templates/logs/search_stats_logs.html:32 +msgid "Search events" +msgstr "Recherche les évènements" + #: logs/templates/logs/sidebar.html:33 msgid "Summary" msgstr "Résumé" @@ -277,10 +319,6 @@ msgstr "Base de données" msgid "Wiring actions" msgstr "Actions de câblage" -#: logs/templates/logs/sidebar.html:53 -msgid "Users" -msgstr "Utilisateurs" - #: logs/templates/logs/sidebar.html:57 msgid "Machine history" msgstr "Historique des machines" @@ -297,138 +335,134 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: logs/views.py:184 +#: logs/views.py:196 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: logs/views.py:187 +#: logs/views.py:199 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: logs/views.py:228 +#: logs/views.py:240 msgid "Category" msgstr "Catégorie" -#: logs/views.py:229 +#: logs/views.py:241 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: logs/views.py:230 +#: logs/views.py:242 msgid "Number of members" msgstr "Nombre d'adhérents" -#: logs/views.py:231 +#: logs/views.py:243 msgid "Number of clubs" msgstr "Nombre de clubs" -#: logs/views.py:235 +#: logs/views.py:247 msgid "Activated users" msgstr "Utilisateurs activés" -#: logs/views.py:241 +#: logs/views.py:253 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: logs/views.py:247 +#: logs/views.py:259 msgid "Archived users" msgstr "Utilisateurs archivés" -#: logs/views.py:253 +#: logs/views.py:265 msgid "Fully archived users" msgstr "Utilisateurs complètement archivés" -#: logs/views.py:263 +#: logs/views.py:275 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:273 +#: logs/views.py:285 msgid "Contributing members" msgstr "Adhérents cotisants" -#: logs/views.py:279 +#: logs/views.py:291 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: logs/views.py:285 +#: logs/views.py:297 msgid "Banned users" msgstr "Utilisateurs bannis" -#: logs/views.py:291 +#: logs/views.py:303 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: logs/views.py:297 +#: logs/views.py:309 msgid "Users with a confirmed email" msgstr "Utilisateurs ayant un mail confirmé" -#: logs/views.py:303 +#: logs/views.py:315 msgid "Users with an unconfirmed email" msgstr "Utilisateurs ayant un mail non confirmé" -#: logs/views.py:309 +#: logs/views.py:321 msgid "Users pending email confirmation" msgstr "Utilisateurs en attente de confirmation du mail" -#: logs/views.py:315 +#: logs/views.py:327 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:329 +#: logs/views.py:341 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:346 +#: logs/views.py:358 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:347 +#: logs/views.py:359 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:348 +#: logs/views.py:360 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:349 +#: logs/views.py:361 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:350 +#: logs/views.py:362 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:351 +#: logs/views.py:363 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:366 +#: logs/views.py:378 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:412 -msgid "Topology" -msgstr "Topologie" - -#: logs/views.py:428 +#: logs/views.py:440 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:453 +#: logs/views.py:465 msgid "rights" msgstr "droits" -#: logs/views.py:482 +#: logs/views.py:494 msgid "actions" msgstr "actions" -#: logs/views.py:532 logs/views.py:583 +#: logs/views.py:544 logs/views.py:595 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: logs/views.py:545 +#: logs/views.py:557 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." -#: logs/views.py:570 logs/views.py:626 +#: logs/views.py:582 logs/views.py:638 msgid "No model found." msgstr "Aucun modèle trouvé." diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index e8abd822..a5ebf1e1 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 3439441d..ea4fe4c9 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 8423e0cb..9f6a2172 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 7bba1fe6..5f757710 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index c69e0148..ec263386 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 5e7b31d0..8d5b6ecb 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 42c96960..aee6fcff 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index 5bba5b46..43a96241 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 6b57f34b..f1a9177e 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-23 21:25+0200\n" +"POT-Creation-Date: 2020-04-24 18:45+0200\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" From 808f2e27906b5057bc8873e1790569dbb453b5f5 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 20:09:18 +0200 Subject: [PATCH 227/490] Work on improving performance when filtering logs --- logs/models.py | 97 +++++++++++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 37 deletions(-) diff --git a/logs/models.py b/logs/models.py index 06003ff4..49404690 100644 --- a/logs/models.py +++ b/logs/models.py @@ -198,9 +198,10 @@ class MachineHistorySearch: except IpList.DoesNotExist: return [] - return filter( - lambda x: x.field_dict["ipv4_id"] == ip_id, - Version.objects.get_for_model(Interface).order_by("revision__date_created") + return ( + Version.objects.get_for_model(Interface) + .filter(serialized_data__icontains='"ipv4": {}'.format(ip_id)) + .order_by("revision__date_created") ) def _get_interfaces_for_mac(self, mac): @@ -209,9 +210,10 @@ class MachineHistorySearch: :return: An iterable object with the Version objects of Interfaces with the given MAC address """ - return filter( - lambda x: str(x.field_dict["mac_address"]) == mac, - Version.objects.get_for_model(Interface).order_by("revision__date_created") + return ( + Version.objects.get_for_model(Interface) + .filter(serialized_data__icontains='"mac_address": "{}"'.format(mac)) + .order_by("revision__date_created") ) def _get_machines_for_interface(self, interface): @@ -221,9 +223,10 @@ class MachineHistorySearch: which the given interface was attributed """ machine_id = interface.field_dict["machine_id"] - return filter( - lambda x: x.field_dict["id"] == machine_id, - Version.objects.get_for_model(Machine).order_by("revision__date_created") + return ( + Version.objects.get_for_model(Machine) + .filter(serialized_data__icontains='"pk": {}'.format(machine_id)) + .order_by("revision__date_created") ) def _get_user_for_machine(self, machine): @@ -355,9 +358,10 @@ class History: # Get all the versions for this instance, with the oldest first self._last_version = None - interface_versions = filter( - lambda x: x.field_dict["id"] == instance_id, - Version.objects.get_for_model(model).order_by("revision__date_created") + interface_versions = ( + Version.objects.get_for_model(model) + .filter(serialized_data__icontains='"pk": {}'.format(instance_id)) + .order_by("revision__date_created") ) for version in interface_versions: @@ -440,11 +444,18 @@ class VersionAction(HistoryEvent): def _previous_version(self): model = self.object_type() try: - return next( - filter( - lambda x: x.field_dict["id"] == self.object_id() and x.revision.date_created < self.version.revision.date_created, - Version.objects.get_for_model(model).order_by("-revision__date_created") + query = ( + Q( + serialized_data__icontains='"pk": {}'.format(self.object_id()) ) + & Q( + revision__date_created_lt=self.version.revision.date_created + ) + ) + return next( + Version.objects.get_for_model(model) + .filter(query) + .order_by("-revision__date_created") ) except StopIteration: return None @@ -584,21 +595,29 @@ class UserHistory(History): self.events = [] # Try to find an Adherent object - adherents = filter( - lambda x: x.field_dict["user_ptr_id"] == user_id, + # If it exists, its id will be the same as the user's + adherents = ( Version.objects.get_for_model(Adherent) + .filter(serialized_data__icontains='"pk": {}'.format(user_id)) ) - obj = next(adherents, None) - model = Adherent + try: + obj = adherents[0] + model = Adherent + except IndexError: + obj = None # Fallback on a Club if obj is None: - clubs = filter( - lambda x: x.field_dict["user_ptr_id"] == user_id, + clubs = ( Version.objects.get_for_model(Club) + .filter(serialized_data__icontains='"pk": {}'.format(user_id)) ) - obj = next(clubs, None) - model = Club + + try: + obj = clubs[0] + model = Club + except IndexError: + obj = None # If nothing was found, abort if obj is None: @@ -606,9 +625,10 @@ class UserHistory(History): # Add in "related" elements the list of Machine objects # that were once owned by this user - self.related = filter( - lambda x: x.field_dict["user_id"] == user_id, - Version.objects.get_for_model(Machine).order_by("revision__date_created") + self.related = ( + Version.objects.get_for_model(Machine) + .filter(serialized_data__icontains='"user": {}'.format(user_id)) + .order_by("-revision__date_created") ) self.related = [RelatedHistory( m.field_dict["name"] or _("None"), @@ -618,9 +638,10 @@ class UserHistory(History): # Get all the versions for this user, with the oldest first self._last_version = None - user_versions = filter( - lambda x: x.field_dict["id"] == user_id, - Version.objects.get_for_model(User).order_by("revision__date_created") + user_versions = ( + Version.objects.get_for_model(User) + .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .order_by("revision__date_created") ) for version in user_versions: @@ -631,9 +652,10 @@ class UserHistory(History): # Do the same thing for the Adherent of Club self._last_version = None - obj_versions = filter( - lambda x: x.field_dict["id"] == user_id, - Version.objects.get_for_model(model).order_by("revision__date_created") + obj_versions = ( + Version.objects.get_for_model(model) + .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .order_by("revision__date_created") ) for version in obj_versions: @@ -696,10 +718,11 @@ class MachineHistory(History): def get(self, machine_id): # Add as "related" histories the list of Interface objects # that were once assigned to this machine - self.related = list(filter( - lambda x: x.field_dict["machine_id"] == machine_id, - Version.objects.get_for_model(Interface).order_by("revision__date_created") - )) + self.related = list( + Version.objects.get_for_model(Interface) + .filter(serialized_data__icontains='"machine": {}'.format(machine_id)) + .order_by("-revision__date_created") + ) # Create RelatedHistory objects and remove duplicates self.related = [RelatedHistory( From b43d4dc77f7834c7dbfc1589a3b7ff5af1c52638 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 18:15:34 +0000 Subject: [PATCH 228/490] Fix bugs introduced while improving performance --- logs/models.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/logs/models.py b/logs/models.py index 49404690..f5796f93 100644 --- a/logs/models.py +++ b/logs/models.py @@ -449,15 +449,13 @@ class VersionAction(HistoryEvent): serialized_data__icontains='"pk": {}'.format(self.object_id()) ) & Q( - revision__date_created_lt=self.version.revision.date_created + revision__date_created__lt=self.version.revision.date_created ) ) - return next( - Version.objects.get_for_model(model) + return (Version.objects.get_for_model(model) .filter(query) - .order_by("-revision__date_created") - ) - except StopIteration: + .order_by("-revision__date_created")[0]) + except Exception as e: return None def _compute_diff(self, v1, v2, ignoring=["pwd_ntlm"]): From 5b687ec203daa6cbf5eabcc9e590e28a732c1b58 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:00:16 +0200 Subject: [PATCH 229/490] Make logs queries more efficient --- logs/models.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/logs/models.py b/logs/models.py index f5796f93..942a76a4 100644 --- a/logs/models.py +++ b/logs/models.py @@ -200,7 +200,7 @@ class MachineHistorySearch: return ( Version.objects.get_for_model(Interface) - .filter(serialized_data__icontains='"ipv4": {}'.format(ip_id)) + .filter(serialized_data__contains='"ipv4": {}'.format(ip_id)) .order_by("revision__date_created") ) @@ -212,7 +212,7 @@ class MachineHistorySearch: """ return ( Version.objects.get_for_model(Interface) - .filter(serialized_data__icontains='"mac_address": "{}"'.format(mac)) + .filter(serialized_data__contains='"mac_address": "{}"'.format(mac)) .order_by("revision__date_created") ) @@ -225,7 +225,7 @@ class MachineHistorySearch: machine_id = interface.field_dict["machine_id"] return ( Version.objects.get_for_model(Machine) - .filter(serialized_data__icontains='"pk": {}'.format(machine_id)) + .filter(serialized_data__contains='"pk": {}'.format(machine_id)) .order_by("revision__date_created") ) @@ -360,7 +360,7 @@ class History: self._last_version = None interface_versions = ( Version.objects.get_for_model(model) - .filter(serialized_data__icontains='"pk": {}'.format(instance_id)) + .filter(serialized_data__contains='"pk": {}'.format(instance_id)) .order_by("revision__date_created") ) @@ -446,7 +446,7 @@ class VersionAction(HistoryEvent): try: query = ( Q( - serialized_data__icontains='"pk": {}'.format(self.object_id()) + serialized_data__contains='"pk": {}'.format(self.object_id()) ) & Q( revision__date_created__lt=self.version.revision.date_created @@ -596,7 +596,7 @@ class UserHistory(History): # If it exists, its id will be the same as the user's adherents = ( Version.objects.get_for_model(Adherent) - .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .filter(serialized_data__contains='"pk": {}'.format(user_id)) ) try: obj = adherents[0] @@ -608,7 +608,7 @@ class UserHistory(History): if obj is None: clubs = ( Version.objects.get_for_model(Club) - .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .filter(serialized_data__contains='"pk": {}'.format(user_id)) ) try: @@ -625,7 +625,7 @@ class UserHistory(History): # that were once owned by this user self.related = ( Version.objects.get_for_model(Machine) - .filter(serialized_data__icontains='"user": {}'.format(user_id)) + .filter(serialized_data__contains='"user": {}'.format(user_id)) .order_by("-revision__date_created") ) self.related = [RelatedHistory( @@ -638,7 +638,7 @@ class UserHistory(History): self._last_version = None user_versions = ( Version.objects.get_for_model(User) - .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .filter(serialized_data__contains='"pk": {}'.format(user_id)) .order_by("revision__date_created") ) @@ -652,7 +652,7 @@ class UserHistory(History): self._last_version = None obj_versions = ( Version.objects.get_for_model(model) - .filter(serialized_data__icontains='"pk": {}'.format(user_id)) + .filter(serialized_data__contains='"pk": {}'.format(user_id)) .order_by("revision__date_created") ) @@ -718,7 +718,7 @@ class MachineHistory(History): # that were once assigned to this machine self.related = list( Version.objects.get_for_model(Interface) - .filter(serialized_data__icontains='"machine": {}'.format(machine_id)) + .filter(serialized_data__contains='"machine": {}'.format(machine_id)) .order_by("-revision__date_created") ) From 1f622d19f171e859ed878704beeb2dd3bdad1e26 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:02:55 +0200 Subject: [PATCH 230/490] Auto format MAC addresses inmachine history search --- logs/models.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/logs/models.py b/logs/models.py index 942a76a4..2641ed24 100644 --- a/logs/models.py +++ b/logs/models.py @@ -26,6 +26,7 @@ from django.utils.translation import ugettext_lazy as _ from django.contrib.auth.models import Group from django.db.models import Q from django.apps import apps +from netaddr import EUI from machines.models import IpList from machines.models import Interface @@ -149,9 +150,16 @@ class MachineHistorySearch: self.events = [] if search_type == "ip": - return self._get_by_ip(search)[::-1] + try: + return self._get_by_ip(search)[::-1] + except: + pass elif search_type == "mac": - return self._get_by_mac(search)[::-1] + try: + search = EUI(search) + return self._get_by_mac(search)[::-1] + except: + pass return None From eb3b295db1c97b4281bbfc6d2549a0c8dd95b6df Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:09:15 +0200 Subject: [PATCH 231/490] Fix searching for malformated IP or MAC addresses in machine history search --- logs/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/logs/models.py b/logs/models.py index 2641ed24..d139b9da 100644 --- a/logs/models.py +++ b/logs/models.py @@ -27,6 +27,7 @@ from django.contrib.auth.models import Group from django.db.models import Q from django.apps import apps from netaddr import EUI +macaddress.fields import default_dialect from machines.models import IpList from machines.models import Interface @@ -156,12 +157,12 @@ class MachineHistorySearch: pass elif search_type == "mac": try: - search = EUI(search) + search = EUI(search, dialect=default_dialect()) return self._get_by_mac(search)[::-1] except: pass - return None + return [] def _add_revision(self, user, machine, interface): """ From 44e9b7942973de27b37881a93d7c91190828ec07 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:14:08 +0200 Subject: [PATCH 232/490] Fix import error in logs/models.py --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index d139b9da..8446b329 100644 --- a/logs/models.py +++ b/logs/models.py @@ -27,7 +27,7 @@ from django.contrib.auth.models import Group from django.db.models import Q from django.apps import apps from netaddr import EUI -macaddress.fields import default_dialect +from macaddress.fields import default_dialect from machines.models import IpList from machines.models import Interface From 6280e86472b35bd220420e0d1b30f1fd49c2f8c3 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:34:40 +0200 Subject: [PATCH 233/490] Fix event type filter in postgre --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index 8446b329..b5be183d 100644 --- a/logs/models.py +++ b/logs/models.py @@ -87,7 +87,7 @@ class ActionsSearch: if c is None: return None - classes += c + classes += c.lower() return classes From 7ad4d08dd2200db6db886d7132e6e4cb10c0f85d Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:36:50 +0200 Subject: [PATCH 234/490] Fix fix event type filter in postgre --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index b5be183d..7af5c11c 100644 --- a/logs/models.py +++ b/logs/models.py @@ -87,7 +87,7 @@ class ActionsSearch: if c is None: return None - classes += c.lower() + classes += list(map(str.lower, c)) return classes From 125ffbbd23edbe1a555b1e7a8b08bf9a59307d20 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 23:51:57 +0200 Subject: [PATCH 235/490] Don't assume an event's userfulness when showing logs --- logs/models.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/logs/models.py b/logs/models.py index 7af5c11c..292eead1 100644 --- a/logs/models.py +++ b/logs/models.py @@ -421,11 +421,6 @@ class VersionAction(HistoryEvent): def __init__(self, version): self.version = version - def is_useful(self): - # Some versions are duplicates, and don't have a reference - # to any object, so ignore them - return self.version.object_id is not None - def name(self): return "{} {}".format(self.model_name().title(), self.version.object_repr) @@ -490,7 +485,6 @@ class RevisionAction: self.performed_by = revision.user self.revision = revision self.versions = [VersionAction(v) for v in revision.version_set.all()] - self.versions = filter(lambda v: v.is_useful(), self.versions) def id(self): return self.revision.id From 4b6f5a14634ffb6c091251817259d62882055599 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 00:13:00 +0200 Subject: [PATCH 236/490] Improve description for events with None objects --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index 292eead1..acf8771b 100644 --- a/logs/models.py +++ b/logs/models.py @@ -422,7 +422,7 @@ class VersionAction(HistoryEvent): self.version = version def name(self): - return "{} {}".format(self.model_name().title(), self.version.object_repr) + return self.version._object_cache or self.version.object_repr def application(self): return self.version.content_type.app_label From 147ff29fb5a904ea228659656b114efb56b5703b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 22:40:21 +0000 Subject: [PATCH 237/490] Add username autocompletion in event filter view --- logs/forms.py | 4 ++-- logs/templates/logs/search_stats_logs.html | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index ae281e98..30780d4a 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -97,9 +97,9 @@ def classes_for_action_type(action_type): class ActionsSearchForm(Form): """The form for a simple search""" - u = forms.CharField( + u = forms.ModelChoiceField( label=_("Performed by"), - max_length=100, + queryset=users.models.User.objects.all(), required=False, ) t = forms.MultipleChoiceField( diff --git a/logs/templates/logs/search_stats_logs.html b/logs/templates/logs/search_stats_logs.html index 5faa8066..5211d336 100644 --- a/logs/templates/logs/search_stats_logs.html +++ b/logs/templates/logs/search_stats_logs.html @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} +{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Search events" %}{% endblock %} @@ -31,10 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Search events" %}

    - {% bootstrap_field actions_form.u %} - {% include 'buttons/multiple_checkbox_alt.html' with field=actions_form.t %} - {% bootstrap_field actions_form.s %} - {% bootstrap_field actions_form.e %} + {% massive_bootstrap_form actions_form 'u' %} {% trans "Search" as tr_search %} {% bootstrap_button tr_search button_type="submit" icon="search" %} From c43c5328221b60dccb47c85939dc4acbeb96cfe0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 12:31:18 +0200 Subject: [PATCH 238/490] Start implementing detailed history for event class --- logs/models.py | 55 ++++++++- logs/templates/logs/detailed_history.html | 2 +- logs/templatetags/logs_extra.py | 3 +- logs/urls.py | 5 - logs/views.py | 104 +++++------------- machines/templates/machines/aff_machines.html | 4 +- templates/buttons/history.html | 6 - users/templates/users/profil.html | 2 +- 8 files changed, 83 insertions(+), 98 deletions(-) diff --git a/logs/models.py b/logs/models.py index acf8771b..112d536f 100644 --- a/logs/models.py +++ b/logs/models.py @@ -92,6 +92,10 @@ class ActionsSearch: return classes +############################ +# Machine history search # +############################ + class MachineHistorySearchEvent: def __init__(self, user, machine, interface, start=None, end=None): """ @@ -280,14 +284,19 @@ class MachineHistorySearch: return self.events +############################ +# Generic history classes # +############################ + class RelatedHistory: - def __init__(self, name, model_name, object_id): + def __init__(self, name, app_name, model_name, object_id): """ :param name: Name of this instance :param model_name: Name of the related model (e.g. "user") :param object_id: ID of the related object """ self.name = "{} (id = {})".format(name, object_id) + self.app_name = app_name self.model_name = model_name self.object_id = object_id @@ -417,6 +426,10 @@ class History: self._last_version = version +############################ +# Revision history # +############################ + class VersionAction(HistoryEvent): def __init__(self, version): self.version = version @@ -496,6 +509,10 @@ class RevisionAction: return self.revision.get_comment() +############################ +# Class-specific history # +############################ + class UserHistoryEvent(HistoryEvent): def _repr(self, name, value): """ @@ -588,7 +605,7 @@ class UserHistory(History): super(UserHistory, self).__init__() self.event_type = UserHistoryEvent - def get(self, user_id): + def get(self, user_id, model): """ :param user_id: int, the id of the user to lookup :return: list or None, a list of UserHistoryEvent, in reverse chronological order @@ -633,7 +650,8 @@ class UserHistory(History): ) self.related = [RelatedHistory( m.field_dict["name"] or _("None"), - "machine", + "machines" + "Machine", m.field_dict["id"]) for m in self.related] self.related = list(dict.fromkeys(self.related)) @@ -716,7 +734,7 @@ class MachineHistory(History): super(MachineHistory, self).__init__() self.event_type = MachineHistoryEvent - def get(self, machine_id): + def get(self, machine_id, model): # Add as "related" histories the list of Interface objects # that were once assigned to this machine self.related = list( @@ -728,7 +746,8 @@ class MachineHistory(History): # Create RelatedHistory objects and remove duplicates self.related = [RelatedHistory( i.field_dict["mac_address"] or _("None"), - "interface", + "machines", + "Interface", i.field_dict["id"]) for i in self.related] self.related = list(dict.fromkeys(self.related)) @@ -782,10 +801,34 @@ class InterfaceHistory(History): super(InterfaceHistory, self).__init__() self.event_type = InterfaceHistoryEvent - def get(self, interface_id): + def get(self, interface_id, model): events = super(InterfaceHistory, self).get(interface_id, Interface) # Update name self.name = self._last_version.field_dict["mac_address"] return events + + +############################ +# History auto-detect # +############################ + +HISTORY_CLASS_MAPPING = { + User: UserHistory, + Machine: MachineHistory, + Interface: InterfaceHistory, + "default": History +} + + +def get_history_class(model): + """ + Find the mos appropriate History subclass to represent + the given model's history + :model: class + """ + try: + return HISTORY_CLASS_MAPPING[model]() + except KeyError: + return HISTORY_CLASS_MAPPING["default"]() diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html index 7aaf67a0..8ffe5d68 100644 --- a/logs/templates/logs/detailed_history.html +++ b/logs/templates/logs/detailed_history.html @@ -82,7 +82,7 @@ with this program; if not, write to the Free Software Foundation, Inc., diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index 2e58cb67..c436c1fa 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -42,7 +42,7 @@ def is_facture(baseinvoice): @register.inclusion_tag("buttons/history.html") -def history_button(instance, text=False, detailed=False, html_class=True): +def history_button(instance, text=False, html_class=True): """Creates the correct history button for an instance. Args: @@ -57,6 +57,5 @@ def history_button(instance, text=False, detailed=False, html_class=True): "name": instance._meta.model_name, "id": instance.id, "text": text, - "detailed": detailed, "class": html_class, } diff --git a/logs/urls.py b/logs/urls.py index 914761bf..d70cc4a8 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -42,11 +42,6 @@ urlpatterns = [ views.history, name="history", ), - url( - r"(?P\w+)/(?P[0-9]+)$", - views.detailed_history, - name="detailed-history", - ), url(r"^stats_general/$", views.stats_general, name="stats-general"), url(r"^stats_models/$", views.stats_models, name="stats-models"), url(r"^stats_users/$", views.stats_users, name="stats-users"), diff --git a/logs/views.py b/logs/views.py index b71187aa..69102b31 100644 --- a/logs/views.py +++ b/logs/views.py @@ -37,7 +37,6 @@ nombre d'objets par models, nombre d'actions par user, etc """ from __future__ import unicode_literals -from itertools import chain from django.urls import reverse from django.shortcuts import render, redirect @@ -105,9 +104,7 @@ from .models import ( ActionsSearch, RevisionAction, MachineHistorySearch, - UserHistory, - MachineHistory, - InterfaceHistory + get_history_class ) from .forms import ( @@ -526,33 +523,24 @@ def stats_search_machine_history(request): return render(request, "logs/search_machine_history.html", {"history_form": history_form}) -def get_history_object(request, model, object_name, object_id, allow_deleted=False): +def get_history_object(request, model, object_name, object_id): """Get the objet of type model with the given object_id Handles permissions and DoesNotExist errors """ - is_deleted = False - try: object_name_id = object_name + "id" kwargs = {object_name_id: object_id} instance = model.get_instance(**kwargs) except model.DoesNotExist: - is_deleted = True instance = None - if is_deleted and not allow_deleted: - messages.error(request, _("Nonexistent entry.")) - return False, redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) - - if is_deleted: - can_view = can_view_app("logs") + if instance is None: + authorized = can_view_app("logs") msg = None else: - can_view, msg, _permissions = instance.can_view(request.user) + authorized, msg, _permissions = instance.can_view(request.user) - if not can_view: + if not authorized: messages.error( request, msg or _("You don't have the right to access this menu.") ) @@ -563,61 +551,14 @@ def get_history_object(request, model, object_name, object_id, allow_deleted=Fal return True, instance -@login_required -def detailed_history(request, object_name, object_id): - """Render a detailed history for a model. - Permissions are handled by get_history_object. - """ - # Only handle objects for which a detailed view exists - if object_name == "user": - model = User - history = UserHistory() - elif object_name == "machine": - model = Machine - history = MachineHistory() - elif object_name == "interface": - model = Interface - history = InterfaceHistory() - else: - raise Http404(_("No model found.")) - - # Get instance and check permissions - can_view, instance = get_history_object(request, model, object_name, object_id, allow_deleted=True) - if not can_view: - return instance - - # Generate the pagination with the objects - max_result = GeneralOption.get_cached_value("pagination_number") - events = history.get(int(object_id)) - - # Events is None if object wasn't found - if events is None: - messages.error(request, _("Nonexistent entry.")) - return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) - - # Add the paginator in case there are many results - events = re2o_paginator(request, events, max_result) - - # Add a title in case the object was deleted - title = instance or "{} ({})".format(history.name, _("Deleted")) - - return render( - request, - "logs/detailed_history.html", - {"title": title, "events": events, "related_history": history.related}, - ) - - @login_required def history(request, application, object_name, object_id): """Render history for a model. The model is determined using the `HISTORY_BIND` dictionnary if none is found, raises a Http404. The view checks if the user is allowed to see the - history using the `can_view` method of the model. - Permissions are handled by get_history_object. + history using the `can_view` method of the model, or the generic + `can_view_app("logs")` for deleted objects (see `get_history_object`). Args: request: The request sent by the user. @@ -637,16 +578,29 @@ def history(request, application, object_name, object_id): except LookupError: raise Http404(_("No model found.")) - can_view, instance = get_history_object(request, model, object_name, object_id) + authorized, instance = get_history_object(request, model, object_name, object_id) if not can_view: return instance - pagination_number = GeneralOption.get_cached_value("pagination_number") - reversions = Version.objects.get_for_object(instance) - if hasattr(instance, "linked_objects"): - for related_object in chain(instance.linked_objects()): - reversions = reversions | Version.objects.get_for_object(related_object) - reversions = re2o_paginator(request, reversions, pagination_number) + history = get_history_class(model) + events = history.get(int(object_id), model) + + # Events is None if object wasn't found + if events is None: + messages.error(request, _("Nonexistent entry.")) + return redirect( + reverse("users:profil", kwargs={"userid": str(request.user.id)}) + ) + + # Generate the pagination with the objects + max_result = GeneralOption.get_cached_value("pagination_number") + events = re2o_paginator(request, events, max_result) + + # Add a default title in case the object was deleted + title = instance or "{} ({})".format(history.name, _("Deleted")) + return render( - request, "re2o/history.html", {"reversions": reversions, "object": instance} + request, + "logs/detailed_history.html", + {"title": title, "events": events, "related_history": history.related}, ) diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 857d2a3a..77b65546 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -67,7 +67,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Create an interface" as tr_create_an_interface %} {% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %} {% acl_end %} - {% history_button machine detailed=True %} + {% history_button machine %} {% can_delete machine %} {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %} {% acl_end %} @@ -161,7 +161,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %} - {% history_button interface detailed=True %} + {% history_button interface %} {% can_delete interface %} {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %} {% acl_end %} diff --git a/templates/buttons/history.html b/templates/buttons/history.html index 2495dec8..71c2c71d 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -24,13 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} -{% if detailed %} - - {% if text %}{% trans "History" %}{% endif %} - -{% else %} {% if text %}{% trans "History" %}{% endif %} -{% endif %} diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index 4fa780a7..4fec3c18 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -176,7 +176,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit the groups" %} {% acl_end %} - {% history_button users text=True detailed=True %} + {% history_button users text=True %}
    From 7549cc32aa5873793b607a2221f877525a74aa1f Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 12:40:10 +0200 Subject: [PATCH 239/490] Fix RelatedHistory init in detailed history --- logs/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/logs/models.py b/logs/models.py index 112d536f..3f42e6e1 100644 --- a/logs/models.py +++ b/logs/models.py @@ -650,8 +650,8 @@ class UserHistory(History): ) self.related = [RelatedHistory( m.field_dict["name"] or _("None"), - "machines" - "Machine", + "machines", + "machine", m.field_dict["id"]) for m in self.related] self.related = list(dict.fromkeys(self.related)) @@ -747,7 +747,7 @@ class MachineHistory(History): self.related = [RelatedHistory( i.field_dict["mac_address"] or _("None"), "machines", - "Interface", + "interface", i.field_dict["id"]) for i in self.related] self.related = list(dict.fromkeys(self.related)) From 698945ec101cfa5b7e08edda117b8e7a11c04cab Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 12:45:36 +0200 Subject: [PATCH 240/490] Remove detaild_history.html file --- logs/templates/logs/detailed_history.html | 94 ----------------------- logs/views.py | 2 +- re2o/templates/re2o/aff_history.html | 90 +++++++++++++++------- re2o/templates/re2o/history.html | 5 +- 4 files changed, 65 insertions(+), 126 deletions(-) delete mode 100644 logs/templates/logs/detailed_history.html diff --git a/logs/templates/logs/detailed_history.html b/logs/templates/logs/detailed_history.html deleted file mode 100644 index 8ffe5d68..00000000 --- a/logs/templates/logs/detailed_history.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends 'logs/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 © 2020 Jean-Romain Garnier - -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 %} -{% load logs_extra %} - -{% block title %}{% trans "History" %}{% endblock %} - -{% block content %} -

    {% blocktrans %}History of {{ title }}{% endblocktrans %}

    - -{% if events %} -
    {{ reversion.object|truncatechars:20 }}{{ reversion.object|classname }}{{ reversion.content_type.model|truncatechars:20 }} {{ revision.user }} {{ revision.date_created }} {{ revision.comment }}
    {% trans "Edited object" %}{% trans "Object type" %}{% include 'buttons/sort.html' with prefix='logs' col='author' text=tr_edited_by %}
    {{ reversion.object|truncatechars:20 }}{{ reversion.content_type.model|truncatechars:20 }}{{ revision.user }} + + {{ version.name }} + + + + {{ revision.user }} + + {{ revision.date_created }} {{ revision.comment }}
    - + {{ version.name }} - - {{ revision.user }} + + {{ revision.performed_by }} {{ revision.date_created }}
    + {% if version.object_id %} {{ version.name }} + {% else %} + {{ version.name }} + {% endif %} + {% if revision.performed_by %} {{ revision.performed_by }} + {% else %} + {{ revision.performed_by }} + {% endif %} {{ revision.date_created }} {{ revision.comment }}{% include 'buttons/sort.html' with prefix='logs' col='author' text=tr_edited_by %}{% include 'buttons/sort.html' with prefix='logs' col='date' text=tr_date_of_editing %}{% trans "Edited" %} {% trans "Comment" %}
    {{ revision.date_created }} + {% for edit in version.edits %} + {% if edit.1 is None and edit.2 is None %} + {{ edit.0 }}
    + {% elif edit.1 is None %} + {{ edit.0 }}: + {{ edit.2 }}
    + {% else %} + {{ edit.0 }}: + {{ edit.1 }} + ➔ {{ edit.2 }}
    + {% endif %} + {% endfor %} +
    {{ revision.comment }} From 2a4bea4bbc8f0147abefbc49e01beda79a61af8b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 18:23:52 +0200 Subject: [PATCH 223/490] Handle object creation in event logs --- logs/models.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/logs/models.py b/logs/models.py index 282d60bc..3b796c84 100644 --- a/logs/models.py +++ b/logs/models.py @@ -430,17 +430,24 @@ class VersionAction(HistoryEvent): def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]): self.previous_version = self._previous_version() + + if self.previous_version is None: + return None, None, None + self.edited_fields = self._compute_diff(self.version, self.previous_version) return super(VersionAction, self).edits(hide) def _previous_version(self): model = self.object_type() - return next( - filter( - lambda x: x.field_dict["id"] == self.object_id() and x.revision.date_created < self.version.revision.date_created, - Version.objects.get_for_model(model).order_by("-revision__date_created") + try: + return next( + filter( + lambda x: x.field_dict["id"] == self.object_id() and x.revision.date_created < self.version.revision.date_created, + Version.objects.get_for_model(model).order_by("-revision__date_created") + ) ) - ) + except StopIteration: + return None def _compute_diff(self, v1, v2): """ From 37ce4f74d36000639fd34aa578d6a2ee1258d0c2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 18:32:39 +0200 Subject: [PATCH 224/490] Fix diff computation in event logs --- logs/models.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/logs/models.py b/logs/models.py index 3b796c84..06003ff4 100644 --- a/logs/models.py +++ b/logs/models.py @@ -449,7 +449,7 @@ class VersionAction(HistoryEvent): except StopIteration: return None - def _compute_diff(self, v1, v2): + def _compute_diff(self, v1, v2, ignoring=["pwd_ntlm"]): """ Find the edited field between two versions :param v1: Version @@ -460,7 +460,7 @@ class VersionAction(HistoryEvent): fields = [] for key in v1.field_dict.keys(): - if v1.field_dict[key] != v2.field_dict[key]: + if key not in ignoring and v1.field_dict[key] != v2.field_dict[key]: fields.append(key) return fields @@ -471,7 +471,8 @@ class RevisionAction: def __init__(self, revision): self.performed_by = revision.user self.revision = revision - self.versions = [VersionAction(v) for v in revision.version_set.all() if v.is_useful()] + self.versions = [VersionAction(v) for v in revision.version_set.all()] + self.versions = filter(lambda v: v.is_useful(), self.versions) def id(self): return self.revision.id From 42d06b2ba39cc8d2ba9fe23edb5c8ed09a75c2ab Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 24 Apr 2020 16:45:37 +0000 Subject: [PATCH 225/490] Limit length of displayed diff --- logs/templates/logs/aff_stats_logs.html | 6 +++--- logs/views.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index ffb296ca..3ba37958 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -71,11 +71,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {{ edit.0 }}
    {% elif edit.1 is None %} {{ edit.0 }}: - {{ edit.2 }}
    + {{ edit.2|truncatechars:50 }}
    {% else %} {{ edit.0 }}: - {{ edit.1 }} - ➔ {{ edit.2 }}
    + {{ edit.1|truncatechars:50 }} + ➔ {{ edit.2|truncatechars:50 }}
    {% endif %} {% endfor %}
    - - - - - - - - - {% for event in events %} - - - - - - - {% endfor %} -
    {% trans "Date" %}{% trans "Performed by" %}{% trans "Edited" %}{% trans "Comment" %}
    {{ event.date }} - {% if event.performed_by %} - - {{ event.performed_by }} - - {% else %} - {% trans "Unknown" %} - {% endif %} - - {% for edit in event.edits %} - {% if edit.1 is None and edit.2 is None %} - {{ edit.0 }}
    - {% elif edit.1 is None %} - {{ edit.0 }}: - {{ edit.2 }}
    - {% else %} - {{ edit.0 }}: - {{ edit.1 }} - ➔ {{ edit.2 }}
    - {% endif %} - {% endfor %} -
    {{ event.comment }}
    - {% include 'pagination.html' with list=events %} -{% else %} -

    {% trans "No event" %}

    -{% endif %} - -{% if related_history %} - -

    {% blocktrans %}Related elements{% endblocktrans %}

    - - -{% endif %} -
    -
    -
    - -{% endblock %} diff --git a/logs/views.py b/logs/views.py index 69102b31..3f9fac5e 100644 --- a/logs/views.py +++ b/logs/views.py @@ -601,6 +601,6 @@ def history(request, application, object_name, object_id): return render( request, - "logs/detailed_history.html", + "re2o/history.html", {"title": title, "events": events, "related_history": history.related}, ) diff --git a/re2o/templates/re2o/aff_history.html b/re2o/templates/re2o/aff_history.html index 6256ff23..b8c8397a 100644 --- a/re2o/templates/re2o/aff_history.html +++ b/re2o/templates/re2o/aff_history.html @@ -6,6 +6,7 @@ quelques clics. Copyright © 2017 Gabriel Détraz Copyright © 2017 Lara Kermarec Copyright © 2017 Augustin Lemesle +Copyright © 2020 Jean-Romain Garnier 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 @@ -23,36 +24,67 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load i18n %} +{% load logs_extra %} - {% if reversions.paginator %} - - {% endif %} - - - +{% if events %} +
    + + + + + + + + + {% for event in events %} - - - + + + + - - {% for rev in reversions %} - - - - - - {% endfor %} -
    {% trans "Date" %}{% trans "Performed by" %}{% trans "Edited" %}{% trans "Comment" %}
    {% trans "Date" %}{% trans "Performed by" %}{% trans "Comment" %}{{ event.date }} + {% if event.performed_by %} + + {{ event.performed_by }} + + {% else %} + {% trans "Unknown" %} + {% endif %} + + {% for edit in event.edits %} + {% if edit.1 is None and edit.2 is None %} + {{ edit.0 }}
    + {% elif edit.1 is None %} + {{ edit.0 }}: + {{ edit.2 }}
    + {% else %} + {{ edit.0 }}: + {{ edit.1 }} + ➔ {{ edit.2 }}
    + {% endif %} + {% endfor %} +
    {{ event.comment }}
    {{ rev.revision.date_created }}{{ rev.revision.user }}{{ rev.revision.comment }}
    + {% endfor %} + + {% include 'pagination.html' with list=events %} +{% else %} +

    {% trans "No event" %}

    +{% endif %} + +{% if related_history %} + +

    {% blocktrans %}Related elements{% endblocktrans %}

    + + +{% endif %} +
    +
    +
    + diff --git a/re2o/templates/re2o/history.html b/re2o/templates/re2o/history.html index 285fd62f..53ccd587 100644 --- a/re2o/templates/re2o/history.html +++ b/re2o/templates/re2o/history.html @@ -7,6 +7,7 @@ quelques clics. Copyright © 2017 Gabriel Détraz Copyright © 2017 Lara Kermarec Copyright © 2017 Augustin Lemesle +Copyright © 2020 Jean-Romain Garnier 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 @@ -29,8 +30,8 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "History" %}{% endblock %} {% block content %} -

    {% blocktrans %}History of {{ object }}{% endblocktrans %}

    - {% include 're2o/aff_history.html' with reversions=reversions %} +

    {% blocktrans %}History of {{ title }}{% endblocktrans %}

    + {% include 're2o/aff_history.html' with events=events related_history=related_history %}


    From 1b6bf87dbc1077d3e4f819e899b501591b03780a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 12:59:01 +0200 Subject: [PATCH 241/490] Make RelatedHistory cleaner --- logs/models.py | 24 ++++++++---------------- re2o/templates/re2o/aff_history.html | 8 +++++++- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/logs/models.py b/logs/models.py index 3f42e6e1..e7d56afa 100644 --- a/logs/models.py +++ b/logs/models.py @@ -289,16 +289,17 @@ class MachineHistorySearch: ############################ class RelatedHistory: - def __init__(self, name, app_name, model_name, object_id): + def __init__(self, version): """ :param name: Name of this instance :param model_name: Name of the related model (e.g. "user") :param object_id: ID of the related object """ - self.name = "{} (id = {})".format(name, object_id) - self.app_name = app_name - self.model_name = model_name - self.object_id = object_id + self.version = version + self.app_name = version.content_type.app_label + self.model_name = version.content_type.model + self.object_id = version.object_id + self.name = version._object_cache or version.object_repr def __eq__(self, other): return ( @@ -646,13 +647,8 @@ class UserHistory(History): self.related = ( Version.objects.get_for_model(Machine) .filter(serialized_data__contains='"user": {}'.format(user_id)) - .order_by("-revision__date_created") ) - self.related = [RelatedHistory( - m.field_dict["name"] or _("None"), - "machines", - "machine", - m.field_dict["id"]) for m in self.related] + self.related = [RelatedHistory(v) for v in self.related] self.related = list(dict.fromkeys(self.related)) # Get all the versions for this user, with the oldest first @@ -744,11 +740,7 @@ class MachineHistory(History): ) # Create RelatedHistory objects and remove duplicates - self.related = [RelatedHistory( - i.field_dict["mac_address"] or _("None"), - "machines", - "interface", - i.field_dict["id"]) for i in self.related] + self.related = [RelatedHistory(v) for v in self.related] self.related = list(dict.fromkeys(self.related)) events = super(MachineHistory, self).get(machine_id, Machine) diff --git a/re2o/templates/re2o/aff_history.html b/re2o/templates/re2o/aff_history.html index b8c8397a..46eb82bc 100644 --- a/re2o/templates/re2o/aff_history.html +++ b/re2o/templates/re2o/aff_history.html @@ -78,7 +78,13 @@ with this program; if not, write to the Free Software Foundation, Inc., From 60a8fd0ed5b49bb111084bc404a6ffc0909dd0ee Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 11:09:22 +0000 Subject: [PATCH 242/490] Improve display of deleted objects in history --- logs/models.py | 17 ++++------------- re2o/templates/re2o/aff_history.html | 2 +- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/logs/models.py b/logs/models.py index e7d56afa..365424d9 100644 --- a/logs/models.py +++ b/logs/models.py @@ -299,7 +299,7 @@ class RelatedHistory: self.app_name = version.content_type.app_label self.model_name = version.content_type.model self.object_id = version.object_id - self.name = version._object_cache or version.object_repr + self.name = version.object_repr def __eq__(self, other): return ( @@ -390,6 +390,7 @@ class History: if self._last_version is None: return None + self.name = self._last_version.object_repr return self.events[::-1] def _compute_diff(self, v1, v2, ignoring=[]): @@ -743,12 +744,7 @@ class MachineHistory(History): self.related = [RelatedHistory(v) for v in self.related] self.related = list(dict.fromkeys(self.related)) - events = super(MachineHistory, self).get(machine_id, Machine) - - # Update name - self.name = self._last_version.field_dict["name"] - - return events + return super(MachineHistory, self).get(machine_id, Machine) class InterfaceHistoryEvent(HistoryEvent): @@ -794,12 +790,7 @@ class InterfaceHistory(History): self.event_type = InterfaceHistoryEvent def get(self, interface_id, model): - events = super(InterfaceHistory, self).get(interface_id, Interface) - - # Update name - self.name = self._last_version.field_dict["mac_address"] - - return events + return super(InterfaceHistory, self).get(interface_id, Interface) ############################ diff --git a/re2o/templates/re2o/aff_history.html b/re2o/templates/re2o/aff_history.html index 46eb82bc..a52ecc38 100644 --- a/re2o/templates/re2o/aff_history.html +++ b/re2o/templates/re2o/aff_history.html @@ -79,7 +79,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for related in related_history %}
  • {% if related.object_id %} - + {{ related.name }} {% else %} From 4b33f5793500d7776407f74c86082e2dd34b2add Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 11:22:39 +0000 Subject: [PATCH 243/490] Make related history more extensive for users and machines --- logs/models.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/logs/models.py b/logs/models.py index 365424d9..56bf962a 100644 --- a/logs/models.py +++ b/logs/models.py @@ -301,6 +301,9 @@ class RelatedHistory: self.object_id = version.object_id self.name = version.object_repr + if self.model_name: + self.name = "{}: {}".format(self.model_name.title(), self.name) + def __eq__(self, other): return ( self.model_name == other.model_name @@ -643,11 +646,14 @@ class UserHistory(History): if obj is None: return None - # Add in "related" elements the list of Machine objects + # Add in "related" elements the list of objects # that were once owned by this user + query = Q(serialized_data__contains='"user": {}'.format(user_id)) + query &= ~Q(serialized_data__contains='"model": "users.user"') self.related = ( - Version.objects.get_for_model(Machine) - .filter(serialized_data__contains='"user": {}'.format(user_id)) + Version.objects.all() + .filter(query) + .order_by("content_type__model") ) self.related = [RelatedHistory(v) for v in self.related] self.related = list(dict.fromkeys(self.related)) @@ -732,12 +738,13 @@ class MachineHistory(History): self.event_type = MachineHistoryEvent def get(self, machine_id, model): - # Add as "related" histories the list of Interface objects - # that were once assigned to this machine - self.related = list( + query = Q(serialized_data__contains='"machine": {}'.format(machine_id)) + query &= ~Q(serialized_data__contains='"model": "machines.machine"') + + self.related = ( Version.objects.get_for_model(Interface) - .filter(serialized_data__contains='"machine": {}'.format(machine_id)) - .order_by("-revision__date_created") + .filter(query) + .order_by("content_type__model") ) # Create RelatedHistory objects and remove duplicates From 7f3749dd292e5f773f11248341f9e951e4b3bd26 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 11:26:18 +0000 Subject: [PATCH 244/490] Remove useless query in detailed history --- logs/models.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/logs/models.py b/logs/models.py index 56bf962a..a9aaa7aa 100644 --- a/logs/models.py +++ b/logs/models.py @@ -648,11 +648,9 @@ class UserHistory(History): # Add in "related" elements the list of objects # that were once owned by this user - query = Q(serialized_data__contains='"user": {}'.format(user_id)) - query &= ~Q(serialized_data__contains='"model": "users.user"') self.related = ( Version.objects.all() - .filter(query) + .filter(serialized_data__contains='"user": {}'.format(user_id)) .order_by("content_type__model") ) self.related = [RelatedHistory(v) for v in self.related] @@ -738,12 +736,9 @@ class MachineHistory(History): self.event_type = MachineHistoryEvent def get(self, machine_id, model): - query = Q(serialized_data__contains='"machine": {}'.format(machine_id)) - query &= ~Q(serialized_data__contains='"model": "machines.machine"') - self.related = ( Version.objects.get_for_model(Interface) - .filter(query) + .filter(serialized_data__contains='"machine": {}'.format(machine_id)) .order_by("content_type__model") ) From b5226277fde942b6f086d610e35acd2b05d32a28 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 25 Apr 2020 13:45:19 +0200 Subject: [PATCH 245/490] Show creation event in detailed history --- logs/models.py | 108 +++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 53 deletions(-) diff --git a/logs/models.py b/logs/models.py index a9aaa7aa..0ac2ac2b 100644 --- a/logs/models.py +++ b/logs/models.py @@ -42,56 +42,6 @@ from topologie.models import Port from .forms import classes_for_action_type -class ActionsSearch: - def get(self, params): - """ - :param params: dict built by the search view - :return: QuerySet of Revision objects - """ - user = params.get("u", None) - start = params.get("s", None) - end = params.get("e", None) - action_types = params.get("t", None) - - query = Q() - - if user: - query &= Q(user__pseudo=user) - - if start: - query &= Q(date_created__gte=start) - - if end: - query &= Q(date_created__lte=end) - - action_models = self.models_for_action_types(action_types) - if action_models: - query &= Q(version__content_type__model__in=action_models) - - return ( - Revision.objects.all() - .filter(query) - .select_related("user") - .prefetch_related("version_set__object") - ) - - def models_for_action_types(self, action_types): - if action_types is None: - return None - - classes = [] - for action_type in action_types: - c = classes_for_action_type(action_type) - - # Selecting "all" removes the filter - if c is None: - return None - - classes += list(map(str.lower, c)) - - return classes - - ############################ # Machine history search # ############################ @@ -323,7 +273,7 @@ class HistoryEvent: """ self.version = version self.previous_version = previous_version - self.edited_fields = edited_fields + self.edited_fields = edited_fields or [] self.date = version.revision.date_created self.performed_by = version.revision.user self.comment = version.revision.get_comment() or None @@ -422,7 +372,8 @@ class History: diff = self._compute_diff(version, self._last_version) # Ignore "empty" events - if not diff: + # but always keep the first event + if not diff and self._last_version: self._last_version = version return @@ -514,6 +465,56 @@ class RevisionAction: return self.revision.get_comment() +class ActionsSearch: + def get(self, params): + """ + :param params: dict built by the search view + :return: QuerySet of Revision objects + """ + user = params.get("u", None) + start = params.get("s", None) + end = params.get("e", None) + action_types = params.get("t", None) + + query = Q() + + if user: + query &= Q(user__pseudo=user) + + if start: + query &= Q(date_created__gte=start) + + if end: + query &= Q(date_created__lte=end) + + action_models = self.models_for_action_types(action_types) + if action_models: + query &= Q(version__content_type__model__in=action_models) + + return ( + Revision.objects.all() + .filter(query) + .select_related("user") + .prefetch_related("version_set__object") + ) + + def models_for_action_types(self, action_types): + if action_types is None: + return None + + classes = [] + for action_type in action_types: + c = classes_for_action_type(action_type) + + # Selecting "all" removes the filter + if c is None: + return None + + classes += list(map(str.lower, c)) + + return classes + + ############################ # Class-specific history # ############################ @@ -704,7 +705,8 @@ class UserHistory(History): ) # Ignore "empty" events like login - if not diff: + # but always keep the first event + if not diff and self._last_version: self._last_version = version return From 8fe8510b97500f123de5ff0bc5ed2983abcf1d59 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Sat, 25 Apr 2020 23:28:08 +0200 Subject: [PATCH 246/490] [Bug] Remove useless import --- logs/forms.py | 1 - 1 file changed, 1 deletion(-) diff --git a/logs/forms.py b/logs/forms.py index 30780d4a..16e7160d 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -31,7 +31,6 @@ import inspect import cotisations.models import machines.models import preferences.models -import tickets.models import topologie.models import users.models From d4df4b242fa773815c2be0a6d5e05d314c711ac2 Mon Sep 17 00:00:00 2001 From: Bombar Maxime Date: Mon, 27 Apr 2020 01:38:29 +0200 Subject: [PATCH 247/490] Fix edition of SRV records --- machines/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/machines/views.py b/machines/views.py index 3291adb3..cc82913b 100644 --- a/machines/views.py +++ b/machines/views.py @@ -955,7 +955,7 @@ def edit_srv(request, srv_instance, **_kwargs): if srv.changed_data: srv.save() messages.success(request, _("The SRV record was edited.")) - return redirect(reverse("machines:1index-extension")) + return redirect(reverse("machines:index-extension")) return form( {"srvform": srv, "action_name": _("Edit")}, "machines/machine.html", request ) From 02a83a77ce40838eb52eb06ef54c1cd816aee900 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 30 Apr 2020 19:59:42 +0200 Subject: [PATCH 248/490] Allow ACL to be used for two instances of the same model. --- re2o/acl.py | 17 ++++++++++------- re2o/mixins.py | 7 ++----- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/re2o/acl.py b/re2o/acl.py index fd149a97..ae193f08 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -47,8 +47,7 @@ def acl_error_message(msg, permissions): message = msg or _("You don't have the right to edit this option.") if groups: return ( - message - + _("You need to be a member of one of these groups: %s.") % groups + message + _("You need to be a member of one of these groups: %s.") % groups ) else: return message + _("No group has the %s permission(s)!") % " or ".join( @@ -124,7 +123,7 @@ ModelC) ``` The view will be called like this: ``` - view(request, instance_of_A, instance_of_b, *args, **kwargs) + view(request, instance_of_A, instance_of_b, *args, **kwargs) ``` where `*args` and `**kwargs` are the original view arguments. """ @@ -153,7 +152,7 @@ ModelC) """The wrapper used for a specific request""" instances = [] - def process_target(target, fields): + def process_target(target, fields, target_id=None): """This function calls the methods on the target and checks for the can_change_`field` method with the given fields. It also stores the instances of models in order to avoid duplicate DB @@ -161,7 +160,7 @@ ModelC) """ if on_instance: try: - target = target.get_instance(*args, **kwargs) + target = target.get_instance(target_id, *args, **kwargs) instances.append(target) except target.DoesNotExist: yield False, _("Nonexistent entry."), [] @@ -175,8 +174,12 @@ ModelC) error_messages = [] warning_messages = [] - for target, fields in group_targets(): - for can, msg, permissions in process_target(target, fields): + for arg_key, (target, fields) in zip(kwargs.keys(), group_targets()): + if on_instance: + target_id = int(kwargs[arg_key]) + else: + target_id = None + for can, msg, permissions in process_target(target, fields, target_id): if not can: error_messages.append(acl_error_message(msg, permissions)) elif msg: diff --git a/re2o/mixins.py b/re2o/mixins.py index 4e42daa1..77382933 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -61,8 +61,7 @@ class FormRevMixin(object): ) elif self.changed_data: reversion.set_comment( - "Field(s) edited: %s" - % ", ".join(field for field in self.changed_data) + "Field(s) edited: %s" % ", ".join(field for field in self.changed_data) ) return super(FormRevMixin, self).save(*args, **kwargs) @@ -93,11 +92,9 @@ class AclMixin(object): return str(cls.__module__).split(".")[0].lower() @classmethod - def get_instance(cls, *_args, **kwargs): + def get_instance(cls, object_id, *_args, **kwargs): """Récupère une instance - :param objectid: Instance id à trouver :return: Une instance de la classe évidemment""" - object_id = kwargs.get(cls.get_classname() + "id") return cls.objects.get(pk=object_id) @classmethod From 3b857457d8b144a388032789170a45ce473d9a93 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 30 Apr 2020 21:55:44 +0200 Subject: [PATCH 249/490] fix history view. --- logs/views.py | 97 +++++++++++++++++++++++++++------------------------ 1 file changed, 51 insertions(+), 46 deletions(-) diff --git a/logs/views.py b/logs/views.py index 3f9fac5e..cf69ae38 100644 --- a/logs/views.py +++ b/logs/views.py @@ -104,13 +104,10 @@ from .models import ( ActionsSearch, RevisionAction, MachineHistorySearch, - get_history_class + get_history_class, ) -from .forms import ( - ActionsSearchForm, - MachineHistorySearchForm -) +from .forms import ActionsSearchForm, MachineHistorySearchForm @login_required @@ -180,7 +177,9 @@ def stats_logs(request): revisions.object_list = [RevisionAction(r) for r in revisions.object_list] return render(request, "logs/stats_logs.html", {"revisions_list": revisions}) - return render(request, "logs/search_stats_logs.html", {"actions_form": actions_form}) + return render( + request, "logs/search_stats_logs.html", {"actions_form": actions_form} + ) @login_required @@ -305,19 +304,29 @@ def stats_general(request): "email_state_verified_users": [ _("Users with a confirmed email"), User.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), - Adherent.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), + Adherent.objects.filter( + email_state=User.EMAIL_STATE_VERIFIED + ).count(), Club.objects.filter(email_state=User.EMAIL_STATE_VERIFIED).count(), ], "email_state_unverified_users": [ _("Users with an unconfirmed email"), - User.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), - Adherent.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), - Club.objects.filter(email_state=User.EMAIL_STATE_UNVERIFIED).count(), + User.objects.filter( + email_state=User.EMAIL_STATE_UNVERIFIED + ).count(), + Adherent.objects.filter( + email_state=User.EMAIL_STATE_UNVERIFIED + ).count(), + Club.objects.filter( + email_state=User.EMAIL_STATE_UNVERIFIED + ).count(), ], "email_state_pending_users": [ _("Users pending email confirmation"), User.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), - Adherent.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), + Adherent.objects.filter( + email_state=User.EMAIL_STATE_PENDING + ).count(), Club.objects.filter(email_state=User.EMAIL_STATE_PENDING).count(), ], "actives_interfaces": [ @@ -449,32 +458,36 @@ def stats_users(request): de bannissement par user, etc""" stats = { User._meta.verbose_name: { - Machine._meta.verbose_name_plural: User.objects.annotate(num=Count("machine")).order_by("-num")[ - :10 - ], - Facture._meta.verbose_name_plural: User.objects.annotate(num=Count("facture")).order_by("-num")[ - :10 - ], - Ban._meta.verbose_name_plural: User.objects.annotate(num=Count("ban")).order_by("-num")[:10], - Whitelist._meta.verbose_name_plural: User.objects.annotate(num=Count("whitelist")).order_by( - "-num" - )[:10], + Machine._meta.verbose_name_plural: User.objects.annotate( + num=Count("machine") + ).order_by("-num")[:10], + Facture._meta.verbose_name_plural: User.objects.annotate( + num=Count("facture") + ).order_by("-num")[:10], + Ban._meta.verbose_name_plural: User.objects.annotate( + num=Count("ban") + ).order_by("-num")[:10], + Whitelist._meta.verbose_name_plural: User.objects.annotate( + num=Count("whitelist") + ).order_by("-num")[:10], _("rights"): User.objects.annotate(num=Count("groups")).order_by("-num")[ :10 ], }, School._meta.verbose_name: { - User._meta.verbose_name_plural: School.objects.annotate(num=Count("user")).order_by("-num")[:10] + User._meta.verbose_name_plural: School.objects.annotate( + num=Count("user") + ).order_by("-num")[:10] }, Paiement._meta.verbose_name: { - User._meta.verbose_name_plural: Paiement.objects.annotate(num=Count("facture")).order_by("-num")[ - :10 - ] + User._meta.verbose_name_plural: Paiement.objects.annotate( + num=Count("facture") + ).order_by("-num")[:10] }, Banque._meta.verbose_name: { - User._meta.verbose_name_plural: Banque.objects.annotate(num=Count("facture")).order_by("-num")[ - :10 - ] + User._meta.verbose_name_plural: Banque.objects.annotate( + num=Count("facture") + ).order_by("-num")[:10] }, } return render(request, "logs/stats_users.html", {"stats_list": stats}) @@ -505,22 +518,15 @@ def stats_search_machine_history(request): if history_form.is_valid(): history = MachineHistorySearch() events = history.get( - history_form.cleaned_data.get("q", ""), - history_form.cleaned_data + history_form.cleaned_data.get("q", ""), history_form.cleaned_data ) max_result = GeneralOption.get_cached_value("pagination_number") - events = re2o_paginator( - request, - events, - max_result - ) + events = re2o_paginator(request, events, max_result) - return render( - request, - "logs/machine_history.html", - { "events": events }, - ) - return render(request, "logs/search_machine_history.html", {"history_form": history_form}) + return render(request, "logs/machine_history.html", {"events": events},) + return render( + request, "logs/search_machine_history.html", {"history_form": history_form} + ) def get_history_object(request, model, object_name, object_id): @@ -528,9 +534,7 @@ def get_history_object(request, model, object_name, object_id): Handles permissions and DoesNotExist errors """ try: - object_name_id = object_name + "id" - kwargs = {object_name_id: object_id} - instance = model.get_instance(**kwargs) + instance = model.get_instance(object_id) except model.DoesNotExist: instance = None @@ -544,8 +548,9 @@ def get_history_object(request, model, object_name, object_id): messages.error( request, msg or _("You don't have the right to access this menu.") ) - return False, redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) + return ( + False, + redirect(reverse("users:profil", kwargs={"userid": str(request.user.id)})), ) return True, instance From 8c7b1bfb4160980c6e9d66274172e189331cf23f Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 30 Apr 2020 22:00:17 +0200 Subject: [PATCH 250/490] Wrong name for basic django permission change --- tickets/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tickets/models.py b/tickets/models.py index 98a006ac..32d8894f 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -197,13 +197,13 @@ class CommentTicket(AclMixin, models.Model): """ Check that the user has the right to edit the ticket comment or that it is the author""" if ( - not user_request.has_perm("tickets.edit_commentticket") + not user_request.has_perm("tickets.change_commentticket") and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by) ): return ( False, _("You don't have the right to edit other tickets comments than yours."), - ("tickets.edit_commentticket",), + ("tickets.change_commentticket",), ) else: return True, None, None From d2b3663d8909a2fc82c9676de61bd2de8ce989bc Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Thu, 30 Apr 2020 22:21:12 +0200 Subject: [PATCH 251/490] More logic permission check in machine --- machines/locale/fr/LC_MESSAGES/django.po | 48 +++++++++++++++++++----- machines/models.py | 26 ++++++------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index a5ebf1e1..204b7463 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-24 18:45+0200\n" +"POT-Creation-Date: 2020-04-30 22:19+0200\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -192,8 +192,7 @@ msgstr "" "Vous avez atteint le nombre maximal d'interfaces que vous pouvez créer vous-" "même (%s)." -#: machines/models.py:189 machines/models.py:1354 machines/models.py:1373 -#: machines/models.py:1476 machines/models.py:1494 +#: machines/models.py:189 msgid "You don't have the right to edit a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier une machine d'un autre utilisateur." @@ -588,9 +587,19 @@ msgstr "" msgid "You don't have the right to edit the machine." msgstr "Vous n'avez pas le droit d'éditer une machine." -#: machines/models.py:1391 machines/models.py:1511 -msgid "You don't have the right to view machines other than yours." -msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." +#: machines/models.py:1354 +msgid "You don't have the right to edit interfaces of another user." +msgstr "" +"Vous n'avez pas le droit de modifier une interface d'un autre utilisateur." + +#: machines/models.py:1373 +msgid "You don't have the right to delete interfaces of another user." +msgstr "" +"Vous n'avez pas le droit de supprimer une interface d'une autre utilisateur." + +#: machines/models.py:1391 +msgid "You don't have the right to view interfaces other than yours." +msgstr "Vous n'avez pas le droit de voir d'autres interfaces que les vôtres." #: machines/models.py:1419 msgid "Can view an IPv6 addresses list object" @@ -612,17 +621,30 @@ msgstr "Listes d'adresses IPv6" msgid "Nonexistent interface." msgstr "Interface inexistante." -#: machines/models.py:1444 machines/models.py:1688 -msgid "You don't have the right to add an alias to a machine of another user." +#: machines/models.py:1444 +msgid "You don't have the right to add ipv6 to a machine of another user." msgstr "" -"Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " -"utilisateur." +"Vous n'avez pas le droit d'ajouter des ipv6 à une machine d'un autre utilisateur." #: machines/models.py:1457 msgid "You don't have the right to change the SLAAC value of an IPv6 address." msgstr "" "Vous n'avez pas le droit de changer la valeur SLAAC d'une adresse IPv6." +#: machines/models.py:1476 +msgid "You don't have the right to edit ipv6 of a machine of another user." +msgstr "" +"Vous n'avez pas le droit de modifier les ipv6 d'une machine d'un autre utilisateur." + +#: machines/models.py:1494 +msgid "You don't have the right to delete ipv6 of a machine of another user." +msgstr "" +"Vous n'avez pas le droit de supprimer les ipv6 d'une machine d'une autre utilisateur." + +#: machines/models.py:1511 +msgid "You don't have the right to view ipv6 of machines other than yours." +msgstr "Vous n'avez pas le droit de voir les ipv6 d'autres machines que les vôtres." + #: machines/models.py:1543 msgid "A SLAAC IP address is already registered." msgstr "Une adresse IP SLAAC est déjà enregistrée." @@ -677,6 +699,12 @@ msgstr "Le nom de domaine %s contient des caractères interdits." msgid "Invalid extension." msgstr "Extension invalide." +#: machines/models.py:1688 +msgid "You don't have the right to add an alias to a machine of another user." +msgstr "" +"Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " +"utilisateur." + #: machines/models.py:1704 #, python-format msgid "" diff --git a/machines/models.py b/machines/models.py index 66866e77..f20fb416 100644 --- a/machines/models.py +++ b/machines/models.py @@ -202,14 +202,14 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): can_user, _message, permissions = self.user.can_edit( self.user, user_request, *args, **kwargs ) - if not (user_request.has_perm("machines.change_interface") and can_user): + if not (user_request.has_perm("machines.delete_interface") and can_user): return ( False, _( "You don't have the right to delete a machine" " of another user." ), - ("machines.change_interface",) + permissions, + ("machines.delete_interface",) + permissions, ) return True, None, None @@ -1351,7 +1351,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.change_interface") and can_user): return ( False, - _("You don't have the right to edit a machine of another" + _("You don't have the right to edit interfaces of another" " user."), ("machines.change_interface",) + permissions, ) @@ -1367,12 +1367,12 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): can_user, _message, permissions = self.machine.user.can_edit( user_request, *args, **kwargs ) - if not (user_request.has_perm("machines.change_interface") and can_user): + if not (user_request.has_perm("machines.delete_interface") and can_user): return ( False, - _("You don't have the right to edit a machine of another" + _("You don't have the right to delete interfaces of another" " user."), - ("machines.change_interface",) + permissions, + ("machines.delete_interface",) + permissions, ) return True, None, None @@ -1388,7 +1388,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ): return ( False, - _("You don't have the right to view machines other than yours."), + _("You don't have the right to view interfaces other than yours."), ("machines.view_interface",), ) return True, None, None @@ -1441,7 +1441,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return ( False, _( - "You don't have the right to add an alias to a" + "You don't have the right to add ipv6 to a" " machine of another user." ), ("machines.add_ipv6list",), @@ -1473,7 +1473,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.change_ipv6list") and can_user): return ( False, - _("You don't have the right to edit a machine of another user."), + _("You don't have the right to edit ipv6 of a machine of another user."), ("machines.change_ipv6list",), ) return True, None, None @@ -1488,11 +1488,11 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): can_user, _message, permissions = self.interface.machine.user.can_edit( user_request, *args, **kwargs ) - if not (user_request.has_perm("machines.change_ipv6list") and can_user): + if not (user_request.has_perm("machines.delete_ipv6list") and can_user): return ( False, - _("You don't have the right to edit a machine of another user."), - ("machines.change_ipv6list",) + permissions, + _("You don't have the right to delete ipv6 of a machine of another user."), + ("machines.delete_ipv6list",) + permissions, ) return True, None, None @@ -1508,7 +1508,7 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ): return ( False, - _("You don't have the right to view machines other than yours."), + _("You don't have the right to view ipv6 of machines other than yours."), ("machines.view_ipv6list",), ) return True, None, None From 2d40c3ecaad00d3d8b1b64cbd7221f0d56f1c406 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 30 Apr 2020 22:27:21 +0200 Subject: [PATCH 252/490] Machine inherits AclMixin --- machines/models.py | 42 +++++++++++++++--------------------------- topologie/models.py | 30 ++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/machines/models.py b/machines/models.py index f20fb416..0c31147c 100644 --- a/machines/models.py +++ b/machines/models.py @@ -62,7 +62,7 @@ from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin -class Machine(RevMixin, FieldPermissionModelMixin, models.Model): +class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model): """ Class définissant une machine, object parent user, objets fils interfaces""" @@ -80,14 +80,6 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): verbose_name = _("machine") verbose_name_plural = _("machines") - @classmethod - def get_instance(cls, machineid, *_args, **_kwargs): - """Get the Machine instance with machineid. - :param userid: The id - :return: The user - """ - return cls.objects.get(pk=machineid) - def linked_objects(self): """Return linked objects : machine and domain. Usefull in history display""" @@ -157,8 +149,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): if user != user_request: return ( False, - _("You don't have the right to add a machine to another" - " user."), + _("You don't have the right to add a machine to another" " user."), ("machines.add_machine",), ) if user.user_interfaces().count() >= max_lambdauser_interfaces: @@ -186,8 +177,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.change_interface") and can_user): return ( False, - _("You don't have the right to edit a machine of another" - " user."), + _("You don't have the right to edit a machine of another" " user."), ("machines.change_interface",) + permissions, ) return True, None, None @@ -225,8 +215,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, models.Model): ): return ( False, - _("You don't have the right to view other machines than" - " yours."), + _("You don't have the right to view other machines than" " yours."), ("machines.view_machine",), ) return True, None, None @@ -558,8 +547,7 @@ class IpType(RevMixin, AclMixin, models.Model): for element in IpType.objects.all().exclude(pk=self.pk): if not self.ip_set.isdisjoint(element.ip_set): raise ValidationError( - _("The specified range is not disjoint from existing" - " ranges.") + _("The specified range is not disjoint from existing" " ranges.") ) # On formate le prefix v6 if self.prefix_v6: @@ -1302,7 +1290,11 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not ( preferences.models.OptionalMachine.get_cached_value("create_machine") ): - return False, _("You don't have the right to add a machine."), ("machines.add_interface",) + return ( + False, + _("You don't have the right to add a machine."), + ("machines.add_interface",), + ) max_lambdauser_interfaces = preferences.models.OptionalMachine.get_cached_value( "max_lambdauser_interfaces" ) @@ -1351,8 +1343,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.change_interface") and can_user): return ( False, - _("You don't have the right to edit interfaces of another" - " user."), + _("You don't have the right to edit a machine of another" " user."), ("machines.change_interface",) + permissions, ) return True, None, None @@ -1370,9 +1361,8 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.delete_interface") and can_user): return ( False, - _("You don't have the right to delete interfaces of another" - " user."), - ("machines.delete_interface",) + permissions, + _("You don't have the right to edit a machine of another" " user."), + ("machines.change_interface",) + permissions, ) return True, None, None @@ -1761,8 +1751,7 @@ class Domain(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ): return ( False, - _("You don't have the right to view other machines than" - " yours."), + _("You don't have the right to view other machines than" " yours."), ("machines.view_domain",), ) return True, None, None @@ -1979,8 +1968,7 @@ class OuverturePortList(RevMixin, AclMixin, models.Model): class Meta: permissions = ( - ("view_ouvertureportlist", _("Can view a ports opening list" - " object")), + ("view_ouvertureportlist", _("Can view a ports opening list" " object")), ) verbose_name = _("ports opening list") verbose_name_plural = _("ports opening lists") diff --git a/topologie/models.py b/topologie/models.py index 34c390bb..445d70e5 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -88,7 +88,7 @@ class Stack(AclMixin, RevMixin, models.Model): ) -class AccessPoint(AclMixin, Machine): +class AccessPoint(Machine): """Define a wireless AP. Inherit from machines.interfaces Definition pour une borne wifi , hérite de machines.interfaces @@ -135,6 +135,12 @@ class AccessPoint(AclMixin, Machine): def __str__(self): return str(self.interface_set.first()) + @classmethod + def get_instance(cls, object_id, *_args, **kwargs): + """Récupère une instance + :return: Une instance de la classe évidemment""" + return cls.objects.get(pk=object_id) + class Server(Machine): """ @@ -173,8 +179,14 @@ class Server(Machine): def __str__(self): return str(self.interface_set.first()) + @classmethod + def get_instance(cls, object_id, *_args, **kwargs): + """Récupère une instance + :return: Une instance de la classe évidemment""" + return cls.objects.get(pk=object_id) -class Switch(AclMixin, Machine): + +class Switch(Machine): """ Definition d'un switch. Contient un nombre de ports (number), un emplacement (location), un stack parent (optionnel, stack) et un id de membre dans le stack (stack_member_id) @@ -457,6 +469,12 @@ class Switch(AclMixin, Machine): def __str__(self): return str(self.get_name) + @classmethod + def get_instance(cls, object_id, *_args, **kwargs): + """Récupère une instance + :return: Une instance de la classe évidemment""" + return cls.objects.get(pk=object_id) + class ModelSwitch(AclMixin, RevMixin, models.Model): """Un modèle (au sens constructeur) de switch""" @@ -532,7 +550,9 @@ class ModuleOnSwitch(AclMixin, RevMixin, models.Model): unique_together = ["slot", "switch"] def __str__(self): - return _("On slot %(slot)s of %(switch)s").format(slot=str(self.slot), switch=str(self.switch)) + return _("On slot %(slot)s of %(switch)s").format( + slot=str(self.slot), switch=str(self.switch) + ) class ConstructorSwitch(AclMixin, RevMixin, models.Model): @@ -842,9 +862,7 @@ class PortProfile(AclMixin, RevMixin, models.Model): radius_type = models.CharField( max_length=32, choices=TYPES, - help_text=_( - "Type of RADIUS authentication: inactive, MAC-address or 802.1X." - ), + help_text=_("Type of RADIUS authentication: inactive, MAC-address or 802.1X."), verbose_name=_("RADIUS type"), ) radius_mode = models.CharField( From 2a7d334db74bf13243ec7ddee72d5b684f78c06c Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 30 Apr 2020 22:32:17 +0200 Subject: [PATCH 253/490] Avoid failing when permissions depending on another model are None. --- cotisations/models.py | 16 ++++++++-------- machines/models.py | 12 ++++++------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index dcb62408..00978275 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -138,7 +138,7 @@ class Facture(BaseInvoice): abstract = False permissions = ( # TODO : change facture to invoice - ("change_facture_control", _("Can edit the \"controlled\" state")), + ("change_facture_control", _('Can edit the "controlled" state')), ("view_facture", _("Can view an invoice object")), ("change_all_facture", _("Can edit all the previous invoices")), ) @@ -166,7 +166,7 @@ class Facture(BaseInvoice): return ( False, _("You don't have the right to edit this user's invoices."), - ("cotisations.change_all_facture",) + permissions, + ("cotisations.change_all_facture",) + (permissions or ()), ) elif not user_request.has_perm("cotisations.change_all_facture") and ( self.control or not self.valid @@ -198,7 +198,7 @@ class Facture(BaseInvoice): return ( False, _("You don't have the right to delete this user's invoices."), - ("cotisations.change_all_facture",) + permissions, + ("cotisations.change_all_facture",) + (permissions or ()), ) elif not user_request.has_perm("cotisations.change_all_facture") and ( self.control or not self.valid @@ -243,7 +243,7 @@ class Facture(BaseInvoice): can = user_request.has_perm("cotisations.change_facture_control") return ( can, - _("You don't have the right to edit the \"controlled\" state.") + _('You don\'t have the right to edit the "controlled" state.') if not can else None, ("cotisations.change_facture_control",), @@ -297,21 +297,21 @@ class Facture(BaseInvoice): for purchase in self.vente_set.all(): if hasattr(purchase, "cotisation"): cotisation = purchase.cotisation - if cotisation.type_cotisation == 'Connexion': + if cotisation.type_cotisation == "Connexion": cotisation.date_start = date_con date_con += relativedelta( months=(purchase.duration or 0) * purchase.number, days=(purchase.duration_days or 0) * purchase.number, ) cotisation.date_end = date_con - elif cotisation.type_cotisation == 'Adhesion': + elif cotisation.type_cotisation == "Adhesion": cotisation.date_start = date_adh date_adh += relativedelta( months=(purchase.duration or 0) * purchase.number, days=(purchase.duration_days or 0) * purchase.number, ) cotisation.date_end = date_adh - else: # it is assumed that adhesion is required for a connexion + else: # it is assumed that adhesion is required for a connexion date = min(date_adh, date_con) cotisation.date_start = date date_adh += relativedelta( @@ -566,7 +566,7 @@ class Vente(RevMixin, AclMixin, models.Model): return ( False, _("You don't have the right to edit this user's purchases."), - ("cotisations.change_all_facture",) + permissions, + ("cotisations.change_all_facture",) + (permissions or ()), ) elif not user_request.has_perm("cotisations.change_all_vente") and ( self.facture.control or not self.facture.valid diff --git a/machines/models.py b/machines/models.py index 0c31147c..348953a6 100644 --- a/machines/models.py +++ b/machines/models.py @@ -178,7 +178,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model): return ( False, _("You don't have the right to edit a machine of another" " user."), - ("machines.change_interface",) + permissions, + ("machines.change_interface",) + (permissions or ()), ) return True, None, None @@ -199,7 +199,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model): "You don't have the right to delete a machine" " of another user." ), - ("machines.delete_interface",) + permissions, + ("machines.change_interface",) + (permissions or ()), ) return True, None, None @@ -1344,7 +1344,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return ( False, _("You don't have the right to edit a machine of another" " user."), - ("machines.change_interface",) + permissions, + ("machines.change_interface",) + (permissions or ()), ) return True, None, None @@ -1362,7 +1362,7 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): return ( False, _("You don't have the right to edit a machine of another" " user."), - ("machines.change_interface",) + permissions, + ("machines.change_interface",) + (permissions or ()), ) return True, None, None @@ -1481,8 +1481,8 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.delete_ipv6list") and can_user): return ( False, - _("You don't have the right to delete ipv6 of a machine of another user."), - ("machines.delete_ipv6list",) + permissions, + _("You don't have the right to edit a machine of another user."), + ("machines.change_ipv6list",) + (permissions or ()), ) return True, None, None From ec7d8e853259eb56761b796870e655634deeba4f Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 30 Apr 2020 22:36:41 +0200 Subject: [PATCH 254/490] PEP8 is for everyone. --- machines/models.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/machines/models.py b/machines/models.py index 348953a6..dc8ba25c 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1361,8 +1361,11 @@ class Interface(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.delete_interface") and can_user): return ( False, - _("You don't have the right to edit a machine of another" " user."), - ("machines.change_interface",) + (permissions or ()), + _( + "You don't have the right to delete interfaces of another" + " user." + ), + ("machines.delete_interface",) + (permissions or ()), ) return True, None, None @@ -1463,7 +1466,9 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.change_ipv6list") and can_user): return ( False, - _("You don't have the right to edit ipv6 of a machine of another user."), + _( + "You don't have the right to edit ipv6 of a machine of another user." + ), ("machines.change_ipv6list",), ) return True, None, None @@ -1481,8 +1486,10 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): if not (user_request.has_perm("machines.delete_ipv6list") and can_user): return ( False, - _("You don't have the right to edit a machine of another user."), - ("machines.change_ipv6list",) + (permissions or ()), + _( + "You don't have the right to delete ipv6 of a machine of another user." + ), + ("machines.delete_ipv6list",) + (permissions or ()), ) return True, None, None @@ -1498,7 +1505,9 @@ class Ipv6List(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): ): return ( False, - _("You don't have the right to view ipv6 of machines other than yours."), + _( + "You don't have the right to view ipv6 of machines other than yours." + ), ("machines.view_ipv6list",), ) return True, None, None From 0156952c1214ec0113b9a04a57603b192bacdc98 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Fri, 1 May 2020 12:23:41 +0200 Subject: [PATCH 255/490] fix acl functions in topologie models inheriting from Machine --- machines/models.py | 1 + topologie/models.py | 69 +++++++++++++++++++----- topologie/templates/topologie/index.html | 2 +- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/machines/models.py b/machines/models.py index dc8ba25c..be88fe96 100644 --- a/machines/models.py +++ b/machines/models.py @@ -130,6 +130,7 @@ class Machine(RevMixin, FieldPermissionModelMixin, AclMixin, models.Model): :param user_request: Utilisateur qui fait la requête :param userid: id de l'user dont on va créer une machine :return: soit True, soit False avec la raison de l'échec""" + raise ValueError("Now you see me.") try: user = users.models.User.objects.get(pk=userid) except users.models.User.DoesNotExist: diff --git a/topologie/models.py b/topologie/models.py index 445d70e5..a184bde4 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -135,11 +135,26 @@ class AccessPoint(Machine): def __str__(self): return str(self.interface_set.first()) + # We want to retrieve the default behaviour given by AclMixin rather + # than the one overwritten by Machine. If you are not familiar with + # the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super + @classmethod - def get_instance(cls, object_id, *_args, **kwargs): - """Récupère une instance - :return: Une instance de la classe évidemment""" - return cls.objects.get(pk=object_id) + def get_instance(cls, *args, **kwargs): + return super(Machine, cls).get_instance(*args, **kwargs) + + @classmethod + def can_create(cls, *args, **kwargs): + return super(Machine, cls).can_create(*args, **kwargs) + + def can_edit(self, *args, **kwargs): + return super(Machine, self).can_edit(*args, **kwargs) + + def can_delete(self, *args, **kwargs): + return super(Machine, self).can_delete(*args, **kwargs) + + def can_view(self, *args, **kwargs): + return super(Machine, self).can_view(*args, **kwargs) class Server(Machine): @@ -179,11 +194,26 @@ class Server(Machine): def __str__(self): return str(self.interface_set.first()) + # We want to retrieve the default behaviour given by AclMixin rather + # than the one overwritten by Machine. If you are not familiar with + # the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super + @classmethod - def get_instance(cls, object_id, *_args, **kwargs): - """Récupère une instance - :return: Une instance de la classe évidemment""" - return cls.objects.get(pk=object_id) + def get_instance(cls, *args, **kwargs): + return super(Machine, cls).get_instance(*args, **kwargs) + + @classmethod + def can_create(cls, *args, **kwargs): + return super(Machine, cls).can_create(*args, **kwargs) + + def can_edit(self, *args, **kwargs): + return super(Machine, self).can_edit(*args, **kwargs) + + def can_delete(self, *args, **kwargs): + return super(Machine, self).can_delete(*args, **kwargs) + + def can_view(self, *args, **kwargs): + return super(Machine, self).can_view(*args, **kwargs) class Switch(Machine): @@ -469,11 +499,26 @@ class Switch(Machine): def __str__(self): return str(self.get_name) + # We want to retrieve the default behaviour given by AclMixin rather + # than the one overwritten by Machine. If you are not familiar with + # the behaviour of `super`, please check https://docs.python.org/3/library/functions.html#super + @classmethod - def get_instance(cls, object_id, *_args, **kwargs): - """Récupère une instance - :return: Une instance de la classe évidemment""" - return cls.objects.get(pk=object_id) + def get_instance(cls, *args, **kwargs): + return super(Machine, cls).get_instance(*args, **kwargs) + + @classmethod + def can_create(cls, *args, **kwargs): + return super(Machine, cls).can_create(*args, **kwargs) + + def can_edit(self, *args, **kwargs): + return super(Machine, self).can_edit(*args, **kwargs) + + def can_delete(self, *args, **kwargs): + return super(Machine, self).can_delete(*args, **kwargs) + + def can_view(self, *args, **kwargs): + return super(Machine, self).can_view(*args, **kwargs) class ModelSwitch(AclMixin, RevMixin, models.Model): diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 4d021779..85424b25 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -56,7 +56,7 @@ function toggle_graph() { - + - {% else %} -

    {% trans "No invoice" %}

    + {% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %} + {% else %} +

    {% trans "No invoice" %}

    {% endif %} diff --git a/machines/templates/machines/aff_profil.html b/machines/templates/machines/aff_profil.html index 4b2650ed..e1afb7cc 100644 --- a/machines/templates/machines/aff_profil.html +++ b/machines/templates/machines/aff_profil.html @@ -34,238 +34,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Machines" %} {{ nb_machines }} - -
    - -
    - {% if machines_list %} -
    - {% if machines_list.paginator %} - {% include 'pagination.html' with list=machines_list go_to_id="machines" %} - {% endif %} - - - - - - - - - - - {% trans "DNS name" as tr_dns_name %} - - - - - - - {% for machine in machines_list %} - - - - - {% for interface in machine.interface_set.all %} - - - - - - - - - - - {% if ipv6_enabled and interface.ipv6 != 'None' %} - - - - {% endif %} - {% if interface.domain.related_domain.all %} - - - - {% endif %} - {% endfor %} - - - - {% endfor %} - -
    {% include 'buttons/sort.html' with prefix='machine' col='name' text=tr_dns_name %}{% trans "Type" %}{% trans "MAC address" %}{% trans "IP address" %}{% trans "Actions" %}
    - {% trans "No name" as tr_no_name %} - {% trans "View the profile" as tr_view_the_profile %} - {% if machine.active %} - - {% else %} - {% trans "Deactivated" %}: - {% endif %} - {{ machine.get_name|default:tr_no_name }} - - {{ machine.user }} - - - {% can_create Interface machine.id %} - {% trans "Create an interface" as tr_create_an_interface %} - {% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %} - {% acl_end %} - {% history_button machine %} - {% can_delete machine %} - {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %} - {% acl_end %} -
    - {% if interface.domain.related_domain.all %} - {{ interface.domain }} - - {% else %} - {{ interface.domain }} - {% endif %} - - {{ interface.machine_type }} - - {{ interface.mac_address }} - - - IPv4 {{ interface.ipv4 }} -
    - {% if ipv6_enabled and interface.ipv6 != 'None' %} - IPv6 - - {% endif %} -
    -
    -
    - - -
    - {% history_button interface %} - {% can_delete interface %} - {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %} - {% acl_end %} -
    -
    -
    -
      -
    • - {{ interface.get_vendor }} -
    • -
    -
    -
    -
    -
      - {% for ipv6 in interface.ipv6.all %} -
    • - {{ ipv6 }} -
    • - {% endfor %} -
    -
    -
    -
    -
      - {% for al in interface.domain.related_domain.all %} -
    • - - {{ al }} - - -
    • - {% endfor %} -
    -
    -
    - - - - {% if machines_list.paginator %} - {% include 'pagination.html' with list=machines_list go_to_id="machines" %} - {% endif %} -
    - {% else %} -

    {% trans "No machine" %}

    - {% endif %} +
    + +
    + {% if machines_list %} + {% include 'machines/aff_machines.html' with machines_list=machines_list %} + {% else %} +

    {%trans "No machines" %}

    + {% endif %}
    From 00e5575a21a3e562b9a1a5c0323ece715dc796ad Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 23 Oct 2020 10:12:18 +0200 Subject: [PATCH 356/490] Update display of ip_types --- machines/templates/machines/aff_iptype.html | 82 +++++++++++++-------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index 7cf710c2..8aaa37f5 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -28,39 +28,59 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load logs_extra %} {% load i18n %} -
    - - - - - - - - - - - - - - - {% for type in iptype_list %} - - - - - - - - - - - - {% endfor %} -
    {% trans "IP type" %}{% trans "Extension" %}{% blocktrans %}"infra" right required{% endblocktrans %}{% trans "IPv4 range" %}{% trans "v6 prefix" %}{% trans "DNSSEC reverse v4/v6" %}{% trans "On VLAN(s)" %}{% trans "Default ports opening" %}
    {{ type.name }}{{ type.extension }}{{ type.need_infra|tick }}{{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}{% if type.ip_network %} on - {{ type.ip_network }}{% endif %}{{ type.prefix_v6 }}/{{ type.prefix_v6_length }}{{ type.reverse_v4|tick }}/{{ type.reverse_v6|tick }}{{ type.vlan }}{{ type.ouverture_ports }} +{% for type in iptype_list %} +
    +
    +
    +
    +
    +

    {{ type.name }}

    +
    +
    +

    {{ type.extension }}

    +
    +
    + {% if type.need_infra %} + {% trans "Infra right required" %} + {% else %} + {% trans "Infra right not required" %} + {% endif %} +
    +
    {% can_edit type %} {% include 'buttons/edit.html' with href='machines:edit-iptype' id=type.id %} {% acl_end %} {% history_button type %} -
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + +
    {% trans "IPv4 range" %}{% trans "v6 prefix" %}{% trans "DNSSEC reverse v4/v6" %}{% trans "On VLAN(s)" %}{% trans "Default ports opening" %}
    {{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}{% if type.ip_network %} + on + {{ type.ip_network }}{% endif %}{{ type.prefix_v6 }}/{{ type.prefix_v6_length }}{{ type.reverse_v4|tick }}/{{ type.reverse_v6|tick }}{{ type.vlan }}{{ type.ouverture_ports }}
    +
    +
    +{% endfor %} + + + From ef890bc3ba8b76cf11787f05f4d8c05a8b9a9afe Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 23 Oct 2020 11:41:02 +0200 Subject: [PATCH 357/490] Add listings of articles to aff of all cotisations --- .../templates/cotisations/aff_cotisations.html | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index b9a1a810..4d2cd3ee 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -59,7 +59,17 @@ with this program; if not, write to the Free Software Foundation, Inc., {% for facture in facture_list %} {{ facture.user }} - {{ facture.name }} + + + {% for article in facture.name_detailed %} + + + + {% endfor %} +
    + {{ article }} +
    + {{ facture.prix_total }} {{ facture.paiement }} {{ facture.date }} From 4fac613373cbeadd7b429d46fdb5eee78a30dca6 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 23 Oct 2020 12:09:09 +0200 Subject: [PATCH 358/490] fix typo in iptypes and update display of switchs --- machines/templates/machines/aff_iptype.html | 7 +- topologie/templates/topologie/aff_switch.html | 91 +++++++++---------- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index 8aaa37f5..4fe373ab 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -28,10 +28,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load logs_extra %} {% load i18n %} +{% if iptype_list.paginator %} +{% include 'pagination.html' with list=iptype_list %} +{% endif %} + + {% for type in iptype_list %}
    -
    +

    {{ type.name }}

    diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index e2866aba..b1407178 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -26,45 +26,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load logs_extra %} {% load i18n %} -
    - {% if switch_list.paginator %} - {% include 'pagination.html' with list=switch_list %} - {% endif %} - - - - - {% trans "DNS name" as tr_dns %} - - {% trans "IPv4 address" as tr_ip %} - - {% trans "Switch bay" as tr_bay %} - - {% trans "Ports" as tr_ports %} - - {% trans "Stack" as tr_stack %} - - - - - - - - {% for switch in switch_list %} - - - - - - - - - - - - {% endfor %} -
    {% include 'buttons/sort.html' with prefix='switch' col='dns' text=tr_dns %}{% include 'buttons/sort.html' with prefix='switch' col='ip' text=tr_ip %}{% include 'buttons/sort.html' with prefix='switch' col='loc' text=tr_bay %}{% include 'buttons/sort.html' with prefix='switch' col='ports' text=tr_ports %}{% include 'buttons/sort.html' with prefix='switch' col='stack' text=tr_stack %}{% trans "Stack member ID" %}{% trans "Switch model" %}{% trans "Details" %}
    - - {{ switch }} +{% for switch in switch_list %} +{{ switch.interface_set.first.ipv4 }}{{ switch.switchbay }}{{ switch.number }}{{ switch.stack.name }}{{ switch.stack_member_id }}{{ switch.model }}{{ switch.interface_set.first.details }} + +
    + {% trans "Switch bay" as tr_bay %} {{switch.switchbay}} +
    +
    {% can_edit switch %} {% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.id %} {% acl_end %} @@ -75,14 +50,38 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_create Port %} {% trans "Creation of ports" as tr_creation %} {% include 'buttons/add.html' with href='topologie:create-ports' id=switch.id desc=tr_creation %} - {% acl_end %} -
    - - {% if switch_list.paginator %} - {% include 'pagination.html' with list=switch_list %} + {% acl_end %} +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + +
    {% trans "IPv4 address"%}{% trans "Ports"%}{% trans "Stack"%}{% trans "Stack member ID" %}{% trans "Switch model" %}
    {{ switch.interface_set.first.ipv4 }}{{ switch.number }}{{ switch.stack.name }}{{ switch.stack_member_id }}{{ switch.model }}
    +
    +
    + {% if switch.interface_set.first.details %} + {% endif %}
    +{% endfor %} From f731038eb27f0fa8170ff347e1df3c7bbb978504 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Fri, 23 Oct 2020 19:13:02 +0200 Subject: [PATCH 359/490] Fix typo and update display port profils --- .../templates/topologie/aff_port_profile.html | 151 +++++++++++------- topologie/templates/topologie/aff_switch.html | 2 +- 2 files changed, 92 insertions(+), 61 deletions(-) diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 009c0b0f..2e5d432d 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -3,7 +3,7 @@ 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 +Copyleft © 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 @@ -24,64 +24,95 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} {% load logs_extra %} -
    - - {% if port_profile_list.paginator %} - {% include 'pagination.html' with list=port_profile_list %} - {% endif %} - - - - - - - - - - - - - - - - {% for port_profile in port_profile_list %} - - - - - - {% endif %} - - - - - - {% endfor %} -
    {% trans "Name" %}{% trans "Default for" %}{% trans "VLANs" %}{% trans "RADIUS settings" %}{% trans "Speed limit" %}{% trans "MAC address limit" %}{% trans "Security" %}
    {{ port_profile.name }}{{ port_profile.profil_default }} {% if port_profile.profil_default%} - {% if port_profile.on_dormitory %}{% blocktrans with dorm=port_profile.on_dormitory %} on {{ dorm }}{% endblocktrans %}{% else %}{% trans "Everywhere" %}{% endif %}{% endif %} - {% if port_profile.vlan_untagged %} - {% trans "Untagged: " %}{{ port_profile.vlan_untagged }} -
    - {% endif %} - {% if port_profile.vlan_tagged.all %} - {% trans "Tagged: " %}{{ port_profile.vlan_tagged.all|join:", " }} - {% endif %} -
    - {% trans "RADIUS type: " %}{{ port_profile.radius_type }} - {% if port_profile.radius_type == "MAC-radius" %} -
    - {% trans "RADIUS mode: " %}{{ port_profile.radius_mode }}
    {{ port_profile.speed }}{{ port_profile.mac_limit }}{{ port_profile.security_parameters_enabled|join:"
    " }}
    - {% can_edit port_profile %} - {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.id %} - {% acl_end %} - {% history_button port_profile %} - {% can_delete port_profile %} - {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.id %} - {% acl_end %} -
    - - {% if port_profile_list.paginator %} - {% include 'pagination.html' with list=port_profile_list %} - {% endif %} +{% for port_profile in port_profile_list %} +
    +
    +
    +
    +
    +

    {{ port_profile.name }}

    +
    +
    + {% can_edit port_profile %} + {% include 'buttons/edit.html' with href='topologie:edit-port-profile' id=port_profile.id %} + {% acl_end %} + {% history_button port_profile %} + {% can_delete port_profile %} + {% include 'buttons/suppr.html' with href='topologie:del-port-profile' id=port_profile.id %} + {% acl_end %} +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + {% endif %} + + + + +
    {% trans "Default for" %}{% trans "RADIUS settings" %}{% trans "Speed limit" %}{% trans "MAC address limit" %}{% trans "Security" %}
    + {{ port_profile.profil_default }} + {% if port_profile.profil_default%} + - {% if port_profile.on_dormitory %}{% blocktrans with dorm=port_profile.on_dormitory %} on {{ dorm }}{% endblocktrans %}{% else %}{% trans "Everywhere" %}{% endif %} + {% endif %} + + {% trans "RADIUS type: " %}{{ port_profile.radius_type }} + {% if port_profile.radius_type == "MAC-radius" %} +
    + {% trans "RADIUS mode: " %}{{ port_profile.radius_mode }}
    {{ port_profile.speed }}{{ port_profile.mac_limit }}{{ port_profile.security_parameters_enabled|join:"
    " }}
    +
    +
    +
    - +{% endfor %} diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index b1407178..a3ce33ed 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -39,7 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% trans "Switch bay" as tr_bay %} {{switch.switchbay}}
    -
    +
    {% can_edit switch %} {% include 'buttons/edit.html' with href='topologie:edit-switch' id=switch.id %} {% acl_end %} From 160230d7a1e9c88b2ba45c165266afd0a45dadcb Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sat, 24 Oct 2020 00:52:25 +0200 Subject: [PATCH 360/490] Add paginator, updates machines, create aff_machines, and minor changes --- machines/templates/machines/aff_iptype.html | 14 +- machines/templates/machines/aff_machines.html | 175 +++++++++--------- machines/templates/machines/aff_portlist.html | 100 ++++++++++ machines/templates/machines/index_iptype.html | 2 + .../templates/machines/index_portlist.html | 51 +---- machines/views.py | 2 + .../templates/multi_op/aff_room_state.html | 111 +++++++---- topologie/templates/topologie/aff_switch.html | 8 + 8 files changed, 283 insertions(+), 180 deletions(-) create mode 100644 machines/templates/machines/aff_portlist.html diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index 4fe373ab..c6123f58 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -29,10 +29,9 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} {% if iptype_list.paginator %} -{% include 'pagination.html' with list=iptype_list %} + {% include 'pagination.html' with list=iptype_list %} {% endif %} - {% for type in iptype_list %}
    @@ -59,8 +58,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    -
    +
    +
    @@ -83,9 +82,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    +
    {% endfor %} - - +{% if iptype_list.paginator %} + {% include 'pagination.html' with list=iptype_list %} +{% endif %} diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 8e738df3..6d97d1c5 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -26,36 +26,22 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load logs_extra %} {% load i18n %} -
    - {% if machines_list.paginator %} - {% include 'pagination.html' with list=machines_list go_to_id="machines" %} - {% endif %} +{% if machines_list.paginator %} + {% include 'pagination.html' with list=machines_list %} +{% endif %} - - - {% trans "DNS name" as tr_dns_name %} - - - - - - - {% for machine in machines_list %} - - - - + + + + +
    +
    +
    {% include 'buttons/sort.html' with prefix='machine' col='name' text=tr_dns_name %}{% trans "Type" %}{% trans "MAC address" %}{% trans "IP address" %}{% trans "Actions" %}
    - {% trans "No name" as tr_no_name %} - {% trans "View the profile" as tr_view_the_profile %} - {% if machine.active %} - - {% else %} - {% trans "Deactivated" %}: - {% endif %} - {{ machine.get_name|default:tr_no_name }} - - {{ machine.user }} - - +{% for machine in machines_list %} +
    +
    +
    +
    +
    + {% trans "No name" as tr_no_name %} + {{ machine.user }} + + {{ machine.get_name|default:tr_no_name }} +
    +
    {% can_create Interface machine.id %} {% trans "Create an interface" as tr_create_an_interface %} {% include 'buttons/add.html' with href='machines:new-interface' id=machine.id desc=tr_create_an_interface %} @@ -64,8 +50,22 @@ with this program; if not, write to the Free Software Foundation, Inc., {% can_delete machine %} {% include 'buttons/suppr.html' with href='machines:del-machine' id=machine.id %} {% acl_end %} -
    + + + + + + + + + {% for interface in machine.interface_set.all %} - - - - {% if ipv6_enabled and interface.ipv6 != 'None' %} - - - - {% endif %} - {% if interface.domain.related_domain.all %} - - - - {% endif %} - {% endfor %} - - - + + + + {% if ipv6_enabled and interface.ipv6 != 'None' %} + + + + {% endif %} + {% if interface.domain.related_domain.all %} + + + + {% endif %} {% endfor %} - -
    {% trans "DNS name"%}{% trans "Type" %}{% trans "MAC address" %}{% trans "IP address" %}{% trans "Actions" %}
    @@ -74,7 +74,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% else %} {{ interface.domain }} @@ -159,59 +159,64 @@ with this program; if not, write to the Free Software Foundation, Inc., {% acl_end %}
    -
    -
      -
    • - {{ interface.get_vendor }} -
    • -
    -
    -
    -
    -
      - {% for ipv6 in interface.ipv6.all %} -
    • - {{ ipv6 }} -
    • - {% endfor %} -
    -
    -
    -
    -
      - {% for al in interface.domain.related_domain.all %} -
    • - - {{ al }} - - -
    • - {% endfor %} -
    -
    -
    +
    +
      +
    • + {{ interface.get_vendor }} +
    • +
    +
    +
    +
    +
      + {% for ipv6 in interface.ipv6.all %} +
    • + {{ ipv6 }} +
    • + {% endfor %} +
    +
    +
    +
    +
      + {% for al in interface.domain.related_domain.all %} +
    • + + {{ al }} + + +
    • + {% endfor %} +
    +
    +
    + +
    +
    + +{% endfor %} - - {% if machines_list.paginator %} - {% include 'pagination.html' with list=machines_list go_to_id="machines" %} - {% endif %} - diff --git a/machines/templates/machines/aff_portlist.html b/machines/templates/machines/aff_portlist.html new file mode 100644 index 00000000..814e0b55 --- /dev/null +++ b/machines/templates/machines/aff_portlist.html @@ -0,0 +1,100 @@ +{% 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 Lara 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 design %} + +{% load acl %} +{% load logs_extra %} +{% load i18n %} + +{% if port_list.paginator %} + {% include 'pagination.html' with list=port_list %} +{% endif %} + +{% for ports in port_list %} +
    +
    +
    +
    +
    +

    {{ ports.name }}

    +
    +
    + {% can_edit ports %} + {% include 'buttons/edit.html' with href='machines:edit-portlist' id=ports.id %} + {% acl_end %} + {% can_delete ports %} + {% include 'buttons/suppr.html' with href='machines:del-portlist' id=ports.id %} + {% acl_end %} +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    {% trans "TCP (input)" %}{% trans "TCP (output)" %}{% trans "UDP (input)" %}{% trans "UDP (output)" %}
    {% for p in ports.tcp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in ports.tcp_ports_out %}{{ p.show_port }}, {% endfor %}{% for p in ports.udp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in ports.udp_ports_out %}{{ p.show_port }}, {% endfor %}
    +
    +
    + +
    +{% endfor %} + +{% if port_list.paginator %} + {% include 'pagination.html' with list=port_list %} +{% endif %} + diff --git a/machines/templates/machines/index_iptype.html b/machines/templates/machines/index_iptype.html index 65f78f74..38a3a8ad 100644 --- a/machines/templates/machines/index_iptype.html +++ b/machines/templates/machines/index_iptype.html @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "Machines" %}{% endblock %} {% block content %} + {% include 'machines/aff_iptype.html' with iptype_list=iptype_list %} {% endblock %} diff --git a/machines/templates/machines/index_portlist.html b/machines/templates/machines/index_portlist.html index 671f8e74..867e51d0 100644 --- a/machines/templates/machines/index_portlist.html +++ b/machines/templates/machines/index_portlist.html @@ -14,52 +14,7 @@ {% trans "Add a configuration" %} {% acl_end %} - - - - - - - - - - - - - {% for pl in port_list %} - - - - - - - - - {% endfor %} -
    {% trans "Name" %}{% trans "TCP (input)" %}{% trans "TCP (output)" %}{% trans "UDP (input)" %}{% trans "UDP (output)" %}{% trans "Machines" %}
    {{ pl.name }}{% for p in pl.tcp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in pl.tcp_ports_out %}{{ p.show_port }}, {% endfor %}{% for p in pl.udp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in pl.udp_ports_out %}{{ p.show_port }}, {% endfor %} - {% if pl.interface_set.all %} - - {% endif %} - - {% can_edit pl %} - {% include 'buttons/edit.html' with href='machines:edit-portlist' id=pl.id %} - {% acl_end %} - {% can_delete pl %} - {% include 'buttons/suppr.html' with href='machines:del-portlist' id=pl.id %} - {% acl_end %} -
    +

    + + {% include 'machines/aff_portlist.html' with port_list=port_list %} {% endblock %} diff --git a/machines/views.py b/machines/views.py index 76d12eca..811bf2a0 100644 --- a/machines/views.py +++ b/machines/views.py @@ -1351,11 +1351,13 @@ def aff_profil(request, user): @can_view_all(IpType) def index_iptype(request): """View used to display the list of existing types of IP.""" + pagination_large_number = GeneralOption.get_cached_value("pagination_large_number") iptype_list = ( IpType.objects.select_related("extension") .select_related("vlan") .order_by("name") ) + iptype_list = re2o_paginator(request, iptype_list, pagination_large_number) return render(request, "machines/index_iptype.html", {"iptype_list": iptype_list}) diff --git a/multi_op/templates/multi_op/aff_room_state.html b/multi_op/templates/multi_op/aff_room_state.html index bdfdd98b..f58e8148 100644 --- a/multi_op/templates/multi_op/aff_room_state.html +++ b/multi_op/templates/multi_op/aff_room_state.html @@ -30,44 +30,79 @@ with this program; if not, write to the Free Software Foundation, Inc., {% include 'pagination.html' with list=room_list %} {% endif %} - - - - {% trans "Room" as tr_room %} - {% trans "Building" as tr_building %} - - - - - - - - - - - {% for room in room_list %} - - - - - - - - - - - {% endfor %} -
    {% include 'buttons/sort.html' with prefix='building' col='name' text=tr_building %}{% include 'buttons/sort.html' with prefix='room' col='name' text=tr_room %}{% trans "Connnected to" %}{% trans "User" %}{% trans "Details" %}{% trans "End of subscription on" %}{% trans "Internet access" %}{% trans "Action" %}
    {{ room.building }}{{ room.name }}{% if room.port_set.all %}{{ asso_name }}{% else %}{% trans "Other operator" %}{% endif %}{% if room.adherent %}{{ room.adherent }}{% else %} {% trans "None" %}{% endif %}{{ room.details }}{% if room.adherent.is_adherent %}{% else %}{% endif %}{% if room.adherent.end_adhesion %}{{ room.adherent.end_adhesion}}{% else %}{% trans "Non member" %}{% endif %} - {% if room.adherent.has_access == True %} - {% trans "Active" %} - {% else %} - {% trans "Disabled" %} - {% endif %} - - {% if room.port_set.all %} - - {% endif %} -
    +{%for room in room_list %} +
    +
    +
    +
    +
    +

    {{ room.building }} - {{room.name }}

    +
    +
    + {% if room.port_set.all %} + + {% endif %} +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + +
    {% trans "User"%}{% trans "Connected to"%}{% trans "End of subscription on" %}{% trans "Internet access" %}
    + {% if room.adherent %} + {{ room.adherent }} + {% else %} + {% trans "None" %} + {% endif %} + + {% if room.port_set.all %} + {{ asso_name }} + {% else %} + {% trans "Other operator" %} + {% endif %} + + {% if room.adherent.is_adherent %} + + {% else %} + + {% endif %} + {% if room.adherent.end_adhesion %} + {{ room.adherent.end_adhesion}} + {% else %} + {% trans "Non member" %} + {% endif %} + + + {% if room.adherent.has_access == True %} + {% trans "Active" %} + {% else %} + {% trans "Disabled" %} + {% endif %} +
    +
    +
    + {% if room.details %} + + {% endif %} +
    +{% endfor %} {% if room_list.paginator %} {% include 'pagination.html' with list=room_list %} diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index a3ce33ed..86c899ba 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -26,6 +26,10 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load logs_extra %} {% load i18n %} +{% if switch_list.paginator %} + {% include 'pagination.html' with list=switch_list %} +{% endif %} + {% for switch in switch_list %}
    @@ -85,3 +89,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% endfor %} +{% if switch_list.paginator %} + {% include 'pagination.html' with list=switch_list %} +{% endif %} + From 5cd671ced3eef8da45c4321f993ece018380d6a8 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Sun, 25 Oct 2020 23:47:59 +0100 Subject: [PATCH 361/490] Turn tables into grids --- machines/templates/machines/aff_machines.html | 288 +++++++++--------- 1 file changed, 140 insertions(+), 148 deletions(-) diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 6d97d1c5..75eb47c6 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -55,162 +55,154 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    - - - - - - - - - - - {% for interface in machine.interface_set.all %} - - - - - - - - - - + {% for interface in machine.interface_set.all %} +
    +
    +
    + {% trans "DNS name"%}: +
    + {% if interface.domain.related_domain.all %} + {{ interface.domain }} + + {% else %} + {{ interface.domain }} + {% endif %} +
    +
    + {% trans "Type" %}: +
    + {{ interface.machine_type }} +
    +
    + {% trans "MAC address" %}: +
    + {{ interface.mac_address }} + +
    +
    + IPv4 {{ interface.ipv4 }} +
    + {% if interface.ipv6.count > 0 %} + IPv6 + + {% else %} + {% trans "No IPv6" %} + {% endif %} +
    +
    +
    + + +
    + {% history_button interface %} + {% can_delete interface %} + {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %} + {% acl_end %} +
    +
    +
    +
    +
    +
    +
      +
    • + {{ interface.get_vendor }} +
    • +
    +
    {% if ipv6_enabled and interface.ipv6 != 'None' %} -
    - - +
    +
      + {% for ipv6 in interface.ipv6.all %} +
    • + {{ ipv6 }} +
    • + {% endfor %} +
    +
    {% endif %} {% if interface.domain.related_domain.all %} - - - +
    +
      + {% for al in interface.domain.related_domain.all %} +
    • + + {{ al }} + + +
    • + {% endfor %} +
    +
    {% endif %} + + + {%if machine.interface_set.count > 1 %} +
    + {% endif %} {% endfor %} -
    {% trans "DNS name"%}{% trans "Type" %}{% trans "MAC address" %}{% trans "IP address" %}{% trans "Actions" %}
    - {% if interface.domain.related_domain.all %} - {{ interface.domain }} - - {% else %} - {{ interface.domain }} - {% endif %} - - {{ interface.machine_type }} - - {{ interface.mac_address }} - - - IPv4 {{ interface.ipv4 }} -
    - {% if ipv6_enabled and interface.ipv6 != 'None' %} - IPv6 - - {% endif %} -
    -
    - - -
    - {% history_button interface %} - {% can_delete interface %} - {% include 'buttons/suppr.html' with href='machines:del-interface' id=interface.id %} - {% acl_end %} -
    -
    -
      -
    • - {{ interface.get_vendor }} -
    • -
    -
    -
    -
    -
      - {% for ipv6 in interface.ipv6.all %} -
    • - {{ ipv6 }} -
    • - {% endfor %} -
    -
    -
    -
    -
      - {% for al in interface.domain.related_domain.all %} -
    • - - {{ al }} - - -
    • - {% endfor %} -
    -
    -
    - + {% endfor %} - - + {% if machines_list.paginator %} {% include 'pagination.html' with list=machines_list %} {% endif %} From e7a1f74755c9030d839795124e9bf242dae5c4f3 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Mon, 26 Oct 2020 19:15:51 +0100 Subject: [PATCH 362/490] Affichage sans chevauchement sur toute la gamme de largeurs --- machines/templates/machines/aff_iptype.html | 57 ++++++++++++--------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index c6123f58..ce436c6b 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -45,9 +45,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% if type.need_infra %} - {% trans "Infra right required" %} + {% trans "Infra right required" %} {% else %} - {% trans "Infra right not required" %} + {% trans "Infra right not required" %} {% endif %}
    @@ -60,27 +60,38 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    - - - - - - - - - - - - - - - - - -
    {% trans "IPv4 range" %}{% trans "v6 prefix" %}{% trans "DNSSEC reverse v4/v6" %}{% trans "On VLAN(s)" %}{% trans "Default ports opening" %}
    {{ type.domaine_ip_start }}-{{ type.domaine_ip_stop }}{% if type.ip_network %} - on - {{ type.ip_network }}{% endif %}{{ type.prefix_v6 }}/{{ type.prefix_v6_length }}{{ type.reverse_v4|tick }}/{{ type.reverse_v6|tick }}{{ type.vlan }}{{ type.ouverture_ports }}
    +
    +
    +
    + {% trans "IPv4 range" %} + {{ type.domaine_ip_start }} - {{ type.domaine_ip_stop }} + {% if type.ip_network %} + on + {{ type.ip_network }}{% endif %} +
    +
    + {% trans "v6 prefix" %} + {{ type.prefix_v6 }} /{{ type.prefix_v6_length }} +
    +
    +
    +
    +
    + {% trans "DNSSEC reverse v4/v6" %} +
    + {{ type.reverse_v4|tick }}/{{ type.reverse_v6|tick }} +
    +
    + {% trans "On VLAN(s)" %} +
    + {{ type.vlan }} +
    +
    + {% trans "Default ports opening" %} +
    + {{ type.ouverture_ports }} +
    +
    From f652606fe422412e4484dcdc9559441305aaa65d Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Mon, 26 Oct 2020 19:23:41 +0100 Subject: [PATCH 363/490] Affichage sans chevauchement sur toute la gamme de largeur --- machines/templates/machines/aff_portlist.html | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/machines/templates/machines/aff_portlist.html b/machines/templates/machines/aff_portlist.html index 814e0b55..b01f407c 100644 --- a/machines/templates/machines/aff_portlist.html +++ b/machines/templates/machines/aff_portlist.html @@ -37,10 +37,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    +

    {{ ports.name }}

    -
    +
    {% can_edit ports %} {% include 'buttons/edit.html' with href='machines:edit-portlist' id=ports.id %} {% acl_end %} @@ -52,23 +52,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    - - - - - - - - - - - - - - - -
    {% trans "TCP (input)" %}{% trans "TCP (output)" %}{% trans "UDP (input)" %}{% trans "UDP (output)" %}
    {% for p in ports.tcp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in ports.tcp_ports_out %}{{ p.show_port }}, {% endfor %}{% for p in ports.udp_ports_in %}{{ p.show_port }}, {% endfor %}{% for p in ports.udp_ports_out %}{{ p.show_port }}, {% endfor %}
    +
    +
    +
    + {% trans "TCP (input)" %} +
    + {% for p in ports.tcp_ports_in %}{{ p.show_port }}, {% endfor %} +
    +
    + {% trans "TCP (output)" %} +
    + {% for p in ports.tcp_ports_out %}{{ p.show_port }}, {% endfor %} +
    +
    + {% trans "UDP (input)" %} +
    + {% for p in ports.udp_ports_in %}{{ p.show_port }}, {% endfor %} +
    +
    + {% trans "UDP (output)" %} +
    + {% for p in ports.udp_ports_out %}{{ p.show_port }}, {% endfor %} +
    +
    -
    - - - - - - - - - - - - - - - - - -
    {% trans "IPv4 address"%}{% trans "Ports"%}{% trans "Stack"%}{% trans "Stack member ID" %}{% trans "Switch model" %}
    {{ switch.interface_set.first.ipv4 }}{{ switch.number }}{{ switch.stack.name }}{{ switch.stack_member_id }}{{ switch.model }}
    +
    +
    +
    + {% trans "IPv4 address"%} +
    + {{ switch.interface_set.first.ipv4 }} +
    +
    + {% trans "Ports"%} +
    + {{ switch.number }} +
    +
    + {% trans "Stack"%} +
    + {{ switch.stack.name }} +
    +
    + {% trans "Stack member ID" %} +
    + {{ switch.stack_member_id }} +
    +
    + {% trans "Switch model" %} +
    + {{ switch.model }} +
    +
    {% if switch.interface_set.first.details %} From 0a1aabd4e290c8fa0204b3eba40ec85d79058ea2 Mon Sep 17 00:00:00 2001 From: grisel-davy Date: Tue, 27 Oct 2020 11:41:46 +0100 Subject: [PATCH 365/490] =?UTF-8?q?Affichage=20am=C3=A9liorer=20avec=202?= =?UTF-8?q?=20footers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- topologie/templates/topologie/aff_port_profile.html | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index 2e5d432d..2e3f5fed 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -54,14 +54,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "RADIUS settings" %} {% trans "Speed limit" %} {% trans "MAC address limit" %} - {% trans "Security" %} {{ port_profile.profil_default }} {% if port_profile.profil_default%} - - {% if port_profile.on_dormitory %}{% blocktrans with dorm=port_profile.on_dormitory %} on {{ dorm }}{% endblocktrans %}{% else %}{% trans "Everywhere" %}{% endif %} + {% if port_profile.on_dormitory %}{% blocktrans with dorm=port_profile.on_dormitory %} on {{ dorm }}{% endblocktrans %}{% else %}{% trans "Everywhere" %}{% endif %} {% endif %} @@ -72,11 +71,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {{ port_profile.speed }} {{ port_profile.mac_limit }} - {{ port_profile.security_parameters_enabled|join:"
    " }}
    + {% if port_profile.security_parameters_enabled %} + + {% endif %} -
    -
    -

    - {% trans "Back to top" %} -

    -

    {{ name_website }} {% trans "powered by" %} Re2o 2016–2020

    -

    - {% blocktrans trimmed %} - Brought to you with . - {% endblocktrans %} - {% trans "About this website" %}. -

    -

    - {% blocktrans trimmed %} - This software is under the terms of the - GPLv2 License. - {% endblocktrans %} -

    -
    -
    + {% include 'footer.html' %} - {# Load JavaScript #} - - - - {% if request.user.shortcuts_enabled %} - - {% endif %} - {# Read the documentation for more information #} - - + {# Load JavaScript #} + + + + {% if request.user.shortcuts_enabled %} + + {% endif %} + {# Read the documentation for more information #} + + + \ No newline at end of file diff --git a/templates/footer.html b/templates/footer.html new file mode 100644 index 00000000..25990629 --- /dev/null +++ b/templates/footer.html @@ -0,0 +1,53 @@ +{% 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 Lara 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 i18n %} +{% load static %} + \ No newline at end of file diff --git a/templates/nav.html b/templates/nav.html new file mode 100644 index 00000000..b48c97c8 --- /dev/null +++ b/templates/nav.html @@ -0,0 +1,322 @@ +{% 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 Lara 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 static %} +{% load acl %} +{% load i18n %} + \ No newline at end of file diff --git a/templates/sidebar.html b/templates/sidebar.html new file mode 100644 index 00000000..08600b30 --- /dev/null +++ b/templates/sidebar.html @@ -0,0 +1,136 @@ +{% 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 Lara 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 i18n %} + +
    + {% if request_user.is_authenticated %} +
    +

    {{ request_user.name }} {{ request_user.surname }}

    +
    + + + + + + + + + + + + + +
    + {% trans "Username" %} +
    + {{ request_user.pseudo }} +
    + {% trans "Room" %} +
    + {{ request_user.room }} +
    + {% trans "Internet access" %} +
    + {% if request_user.has_access %} + {% blocktrans with end_access_date=request.user.end_access|date:"d b Y" %}Until + {{ end_access_date }}{% endblocktrans %} + {% else %} + {% trans "Disabled" %} + {% endif %} +
    + {% trans "Membership" %} +
    + {% if request_user.is_adherent %} + {% blocktrans with end_adhesion_date=request_user.end_adhesion|date:"d b Y" %}Until + {{ end_adhesion_date }}{% endblocktrans %} + {% else %} + {% trans "Non member" %} + {% endif %} +
    + + + + + + + + + + + + + + + + + + +
    {% trans "Username" %}{{ request_user.pseudo }}
    {% trans "Room" %}{{ request_user.room }}
    {% trans "Internet access" %} + {% if request_user.has_access %} + {% blocktrans with end_access_date=request.user.end_access|date:"d b Y" %}Until + {{ end_access_date }}{% endblocktrans %} + {% else %} + {% trans "Disabled" %} + {% endif %} +
    {% trans "Membership" %} + {% if request_user.is_adherent %} + {% blocktrans with end_adhesion_date=request_user.end_adhesion|date:"d b Y" %}Until + {{ end_adhesion_date }}{% endblocktrans %} + {% else %} + {% trans "Non member" %} + {% endif %} +
    + + {% else %} +
    +

    {% trans "You are not logged in." %}

    +
    + {% endif %} +
    +{% if request_user.is_authenticated %} +
    +
    +

    {% blocktrans count interfaces|length as nb %}{{ nb }} active machine{% plural %}{{ nb }} + active machines{% endblocktrans %}

    +
    + +
    +{% endif %} \ No newline at end of file diff --git a/topologie/templates/topologie/index_building.html b/topologie/templates/topologie/index_building.html new file mode 100644 index 00000000..d653a032 --- /dev/null +++ b/topologie/templates/topologie/index_building.html @@ -0,0 +1,42 @@ +{% 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 © 2017 Gabriel Détraz +Copyright © 2017 Lara 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 acl %} +{% load i18n %} + +{% block title %}{% trans "Topology" %}{% endblock %} + +{% block content %} +

    {% trans "Buildings" %}

    + {% can_create Building %} + + {% trans "Add a building" %} + +
    + {% acl_end %} + {% include 'topologie/aff_building.html' with building_list=building_list %} +{% endblock %} + diff --git a/topologie/templates/topologie/index_physical_grouping.html b/topologie/templates/topologie/index_dormitory.html similarity index 60% rename from topologie/templates/topologie/index_physical_grouping.html rename to topologie/templates/topologie/index_dormitory.html index cea4fb75..c137bb70 100644 --- a/topologie/templates/topologie/index_physical_grouping.html +++ b/topologie/templates/topologie/index_dormitory.html @@ -30,33 +30,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block title %}{% trans "Topology" %}{% endblock %} {% block content %} -

    {% trans "Stacks" %}

    - {% can_create Stack %} - - {% trans "Add a stack" %} - - {% acl_end %} - {% include 'topologie/aff_stacks.html' with stack_list=stack_list %} - -

    {% trans "Switch bays" %}

    - {% can_create SwitchBay %} - - {% trans "Add a switch bay" %} - -
    - {% acl_end %} - {% include 'topologie/aff_switch_bay.html' with switch_bay_list=switch_bay_list %} - -

    {% trans "Buildings" %}

    - {% can_create Building %} - - {% trans "Add a building" %} - -
    - {% acl_end %} - {% include 'topologie/aff_building.html' with building_list=building_list %} - -

    {% trans "Dormitories" %}

    {% can_create Dormitory %} diff --git a/topologie/templates/topologie/index_stack.html b/topologie/templates/topologie/index_stack.html new file mode 100644 index 00000000..6006167f --- /dev/null +++ b/topologie/templates/topologie/index_stack.html @@ -0,0 +1,41 @@ +{% 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 © 2017 Gabriel Détraz +Copyright © 2017 Lara 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 acl %} +{% load i18n %} + +{% block title %}{% trans "Topology" %}{% endblock %} + +{% block content %} +

    {% trans "Stacks" %}

    + {% can_create Stack %} +
    + {% trans "Add a stack" %} + + {% acl_end %} + {% include 'topologie/aff_stacks.html' with stack_list=stack_list %} +{% endblock %} + diff --git a/topologie/templates/topologie/index_switch_bay.html b/topologie/templates/topologie/index_switch_bay.html new file mode 100644 index 00000000..f3613e48 --- /dev/null +++ b/topologie/templates/topologie/index_switch_bay.html @@ -0,0 +1,42 @@ +{% 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 © 2017 Gabriel Détraz +Copyright © 2017 Lara 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 acl %} +{% load i18n %} + +{% block title %}{% trans "Topology" %}{% endblock %} + +{% block content %} +

    {% trans "Switch bays" %}

    + {% can_create SwitchBay %} + + {% trans "Add a switch bay" %} + +
    + {% acl_end %} + {% include 'topologie/aff_switch_bay.html' with switch_bay_list=switch_bay_list %} +{% endblock %} + diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index f90e5d55..751fff3c 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -23,36 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. {% endcomment %} -{% load i18n %} {% block sidebar %} - - - {% trans "Rooms and premises" %} - - - - {% trans "Switches" %} - - - - {% trans "Switch modules" %} - - - - {% trans "Port profiles" %} - - - - {% trans "Access points" %} - - - - {% trans "Physical grouping" %} - - - - {% trans "Switch models and constructors" %} - {% endblock %} - diff --git a/topologie/urls.py b/topologie/urls.py index 4baf8234..f387c226 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -49,9 +49,24 @@ urlpatterns = [ url(r"^edit_switch/(?P[0-9]+)$", views.edit_switch, name="edit-switch"), url(r"^new_stack/$", views.new_stack, name="new-stack"), url( - r"^index_physical_grouping/$", - views.index_physical_grouping, - name="index-physical-grouping", + r"^index_stack/$", + views.index_stack, + name="index-stack", + ), + url( + r"^index_switch_bay/$", + views.index_switch_bay, + name="index-switch-bay", + ), + url( + r"^index_building/$", + views.index_building, + name="index-building", + ), + url( + r"^index_dormitory/$", + views.index_dormitory, + name="index-dormitory", ), url(r"^edit_stack/(?P[0-9]+)$", views.edit_stack, name="edit-stack"), url(r"^del_stack/(?P[0-9]+)$", views.del_stack, name="del-stack"), diff --git a/topologie/views.py b/topologie/views.py index 3b1aad30..06eaf410 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -242,35 +242,12 @@ def index_ap(request): @login_required -@can_view_all(Stack, Building, Dormitory, SwitchBay) -def index_physical_grouping(request): - """View used to display the list of stacks (display all switches).""" - stack_list = Stack.objects.prefetch_related( - "switch_set__interface_set__domain__extension" - ) - building_list = Building.objects.all().select_related("dormitory") - dormitory_list = Dormitory.objects.all().prefetch_related("building_set") +@can_view_all(SwitchBay) +def index_switch_bay(request): + """View used to display the list of switch bays.""" switch_bay_list = SwitchBay.objects.select_related( "building__dormitory" ).prefetch_related("switch_set__interface_set__domain") - stack_list = SortTable.sort( - stack_list, - request.GET.get("col"), - request.GET.get("order"), - SortTable.TOPOLOGIE_INDEX_STACK, - ) - building_list = SortTable.sort( - building_list, - request.GET.get("col"), - request.GET.get("order"), - SortTable.TOPOLOGIE_INDEX_BUILDING, - ) - dormitory_list = SortTable.sort( - dormitory_list, - request.GET.get("col"), - request.GET.get("order"), - SortTable.TOPOLOGIE_INDEX_DORMITORY, - ) switch_bay_list = SortTable.sort( switch_bay_list, request.GET.get("col"), @@ -279,15 +256,64 @@ def index_physical_grouping(request): ) return render( request, - "topologie/index_physical_grouping.html", + "topologie/index_switch_bay.html", { - "stack_list": stack_list, "switch_bay_list": switch_bay_list, - "building_list": building_list, - "dormitory_list": dormitory_list, }, ) +@login_required +@can_view_all(Stack) +def index_stack(request): + """View used to display the list of stacks (display all switches).""" + stack_list = Stack.objects.prefetch_related( + "switch_set__interface_set__domain__extension" + ) + return render( + request, + "topologie/index_stack.html", + { + "stack_list": stack_list, + }, + ) + +@login_required +@can_view_all(Building) +def index_building(request): + """View used to display the list of buildings""" + building_list = Building.objects.all().select_related("dormitory") + building_list = SortTable.sort( + building_list, + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_BUILDING, + ) + return render( + request, + "topologie/index_building.html", + { + "building_list": building_list, + }, + ) + +@login_required +@can_view_all(Dormitory) +def index_dormitory(request): + """View used to display the list of dormitories.""" + dormitory_list = Dormitory.objects.all().prefetch_related("building_set") + dormitory_list = SortTable.sort( + dormitory_list, + request.GET.get("col"), + request.GET.get("order"), + SortTable.TOPOLOGIE_INDEX_DORMITORY, + ) + return render( + request, + "topologie/index_dormitory.html", + { + "dormitory_list": dormitory_list, + }, + ) @login_required @can_view_all(ModelSwitch, ConstructorSwitch) @@ -440,7 +466,7 @@ def new_stack(request): if stack.is_valid(): stack.save() messages.success(request, _("The stack was created.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-stack")) return form( {"topoform": stack, "action_name": _("Add")}, "topologie/topo.html", request ) @@ -455,7 +481,7 @@ def edit_stack(request, stack, **_kwargs): if stack.changed_data: stack.save() messages.success(request, _("The stack was edited.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-stack")) return form( {"topoform": stack, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -480,7 +506,7 @@ def del_stack(request, stack, **_kwargs): % stack ), ) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-stack")) return form({"objet": stack}, "topologie/delete.html", request) @@ -847,7 +873,7 @@ def new_switch_bay(request): if switch_bay.is_valid(): switch_bay.save() messages.success(request, _("The switch bay was created.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-switch-bay")) return form( {"topoform": switch_bay, "action_name": _("Add")}, "topologie/topo.html", @@ -864,7 +890,7 @@ def edit_switch_bay(request, switch_bay, **_kwargs): if switch_bay.changed_data: switch_bay.save() messages.success(request, _("The switch bay was edited.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-switch-bay")) return form( {"topoform": switch_bay, "action_name": _("Edit")}, "topologie/topo.html", @@ -891,7 +917,7 @@ def del_switch_bay(request, switch_bay, **_kwargs): % switch_bay ), ) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-switch-bay")) return form( {"objet": switch_bay, "objet_name": _("switch bay")}, "topologie/delete.html", @@ -907,7 +933,7 @@ def new_building(request): if building.is_valid(): building.save() messages.success(request, _("The building was created.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-building")) return form( {"topoform": building, "action_name": _("Add")}, "topologie/topo.html", @@ -924,7 +950,7 @@ def edit_building(request, building, **_kwargs): if building.changed_data: building.save() messages.success(request, _("The building was edited.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-building")) return form( {"topoform": building, "action_name": _("Edit")}, "topologie/topo.html", request ) @@ -949,7 +975,7 @@ def del_building(request, building, **_kwargs): % building ), ) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-building")) return form( {"objet": building, "objet_name": _("building")}, "topologie/delete.html", @@ -965,7 +991,7 @@ def new_dormitory(request): if dormitory.is_valid(): dormitory.save() messages.success(request, _("The dormitory was created.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-dormitory")) return form( {"topoform": dormitory, "action_name": _("Add")}, "topologie/topo.html", @@ -982,7 +1008,7 @@ def edit_dormitory(request, dormitory, **_kwargs): if dormitory.changed_data: dormitory.save() messages.success(request, _("The dormitory was edited.")) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-dormitory")) return form( {"topoform": dormitory, "action_name": _("Edit")}, "topologie/topo.html", @@ -1009,7 +1035,7 @@ def del_dormitory(request, dormitory, **_kwargs): % dormitory ), ) - return redirect(reverse("topologie:index-physical-grouping")) + return redirect(reverse("topologie:index-dormitory")) return form( {"objet": dormitory, "objet_name": _("dormitory")}, "topologie/delete.html", diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html index d2ee77ff..54d83af3 100644 --- a/users/templates/users/sidebar.html +++ b/users/templates/users/sidebar.html @@ -26,73 +26,5 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} {% block sidebar %} - {% if request.user.is_authenticated%} - {% can_create Club %} - - - {% trans "Create a club or organisation" %} - - {% acl_end %} - {% can_create Adherent %} - - - {% trans "Create a user" %} - - {% acl_end %} - {% endif %} - {% can_view_all Club %} - - - {% trans "Clubs and organisations" %} - - {% acl_end %} - {% can_view_all Adherent %} - - - {% trans "Users" %} - - {% acl_end %} - {% can_view_all Ban %} - - - {% trans "Bans" %} - - {% acl_end %} - {% can_view_all Whitelist %} - - - {% trans "Whitelists" %} - - {% acl_end %} - {% can_view_all School %} - - - {% trans "Schools" %} - - {% acl_end %} - {% can_view_all ListShell %} - - - {% trans "Shells" %} - - {% acl_end %} - {% can_view_all ListRight %} - - - {% trans "Groups of rights" %} - - {% acl_end %} - {% can_view_all ServiceUser %} - - - {% trans "Service users" %} - - {% acl_end %} - {% can_change User state %} - - - {% trans "Massively archive" %} - - {% acl_end %} {% endblock %} From cf11e554a15ce7ca69097a4dd634dd05adf40d49 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Mon, 16 Nov 2020 19:11:22 +0100 Subject: [PATCH 376/490] Link in navbar for multi_op app --- multi_op/templates/multi_op/navbar.html | 15 ++++++++++++++- multi_op/templates/multi_op/sidebar.html | 13 ------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/multi_op/templates/multi_op/navbar.html b/multi_op/templates/multi_op/navbar.html index ac3cfe5f..78fd451b 100644 --- a/multi_op/templates/multi_op/navbar.html +++ b/multi_op/templates/multi_op/navbar.html @@ -1,2 +1,15 @@ {% load i18n %} -
  • {% trans "Manage the operators" %}
  • +
  • + {% trans "Multi op" %} » + +
  • \ No newline at end of file diff --git a/multi_op/templates/multi_op/sidebar.html b/multi_op/templates/multi_op/sidebar.html index 86771de1..7de04192 100644 --- a/multi_op/templates/multi_op/sidebar.html +++ b/multi_op/templates/multi_op/sidebar.html @@ -26,18 +26,5 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load i18n %} {% block sidebar %} - - - {% trans "Room connections state" %} - - - - {% trans "Sockets to connect" %} - - - - {% trans "Sockets to disconnect" %} - - {% endblock %} From 8c3df067c40d50e8bf6b8c1dd89c163ce03022f9 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Mon, 16 Nov 2020 19:12:36 +0100 Subject: [PATCH 377/490] Add right sidebar for large screenn, remove scroll in navbar, add scroll in left sidebar --- static/css/base.css | 7 +++++++ templates/base.html | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/static/css/base.css b/static/css/base.css index 14032f4e..0e90f8bd 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -1,3 +1,6 @@ +.navbar-collapse { + overflow-x: hidden; +} /* For the footer to be at the bottom*/ body { padding-top: 50px; @@ -84,11 +87,15 @@ a > i.fa { position: fixed; top:50px; right:0; + overflow-y: auto; + bottom:0px; } .sidenav-left { position: fixed; top:50px; left:0; + overflow-y: auto; + bottom:0px; } .dropdown-menu .dropdown-toggle:after { border-top: .3em solid transparent; diff --git a/templates/base.html b/templates/base.html index d8a2f87b..74d1bb29 100644 --- a/templates/base.html +++ b/templates/base.html @@ -85,11 +85,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block sidebar %} {% endblock %} -
    +
    {# Display django.contrib.messages as Bootstrap alerts #} {% bootstrap_messages %} {% block content %}{% endblock %}
    +
    +
    From 892ac7e958da979627203dd513d5f62c6071d5f0 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Tue, 17 Nov 2020 09:58:08 +0100 Subject: [PATCH 378/490] Bootom navbar don't hide sidebar --- static/css/base.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/css/base.css b/static/css/base.css index 0e90f8bd..25faf52b 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -88,14 +88,14 @@ a > i.fa { top:50px; right:0; overflow-y: auto; - bottom:0px; + bottom:50px; } .sidenav-left { position: fixed; top:50px; left:0; overflow-y: auto; - bottom:0px; + bottom:50px; } .dropdown-menu .dropdown-toggle:after { border-top: .3em solid transparent; From 682d824121cef9d828d383a7885cd1193fd469ca Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Tue, 17 Nov 2020 13:54:33 +0100 Subject: [PATCH 379/490] Add custom themes --- .gitignore | 4 ++++ static/css/themes/default.css | 0 templates/base.html | 6 ++++++ themes/README.md | 23 +++++++++++++++++++++++ themes/cerulan.css | 11 +++++++++++ themes/cosmo.css | 11 +++++++++++ themes/cyborg.css | 11 +++++++++++ themes/darkly.css | 11 +++++++++++ themes/default.css | 0 themes/flatly.css | 11 +++++++++++ themes/journal.css | 11 +++++++++++ themes/lumen.css | 11 +++++++++++ themes/paper.css | 11 +++++++++++ themes/readable.css | 11 +++++++++++ themes/sandstone.css | 11 +++++++++++ themes/simplex.css | 11 +++++++++++ themes/slate.css | 11 +++++++++++ themes/spacelab.css | 11 +++++++++++ themes/superhero.css | 11 +++++++++++ themes/united.css | 11 +++++++++++ themes/yeti.css | 11 +++++++++++ users/forms.py | 17 +++++++++++++++++ users/migrations/0095_user_theme.py | 20 ++++++++++++++++++++ users/models.py | 9 +++++++++ users/templates/users/profil.html | 8 ++++++++ users/urls.py | 1 + users/views.py | 25 +++++++++++++++++++++++++ 27 files changed, 289 insertions(+) create mode 100644 static/css/themes/default.css create mode 100644 themes/README.md create mode 100644 themes/cerulan.css create mode 100644 themes/cosmo.css create mode 100644 themes/cyborg.css create mode 100644 themes/darkly.css create mode 100644 themes/default.css create mode 100644 themes/flatly.css create mode 100644 themes/journal.css create mode 100644 themes/lumen.css create mode 100644 themes/paper.css create mode 100644 themes/readable.css create mode 100644 themes/sandstone.css create mode 100644 themes/simplex.css create mode 100644 themes/slate.css create mode 100644 themes/spacelab.css create mode 100644 themes/superhero.css create mode 100644 themes/united.css create mode 100644 themes/yeti.css create mode 100644 users/migrations/0095_user_theme.py diff --git a/.gitignore b/.gitignore index e63fc69f..80202a96 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,7 @@ settings_local.py local_routers.py re2o.png media/ + +# themes +static/css/themes/* +!static/css/themes/default.css \ No newline at end of file diff --git a/static/css/themes/default.css b/static/css/themes/default.css new file mode 100644 index 00000000..e69de29b diff --git a/templates/base.html b/templates/base.html index 74d1bb29..f4692e1b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -56,6 +56,12 @@ with this program; if not, write to the Free Software Foundation, Inc., + {# load theme #} + {% if request.user.is_authenticated %} + + {% else %} + + {% endif %} {# Favicon with iOS, Android, touchbar support #} diff --git a/themes/README.md b/themes/README.md new file mode 100644 index 00000000..fc0c155c --- /dev/null +++ b/themes/README.md @@ -0,0 +1,23 @@ +# Custom themes for Re2o + +The following themes are licensed under MIT to Thomas Park. See https://bootswatch.com. + +By default, only the default.css is enabled, which is a blank css file. + +**How to activate new themes ?** + +You can activate themes by copying them, or making a symbolic link to the `static/css/themes` directory and collecting the statics. + +**How to change the default theme ?** + +You can change the default theme by changing the default.css file. + +**How to add new theme ?** + +You can add a brand new theme by adding a css file to the `static/css/themes` directory and collecting the statics. + +**What happens if I delete a theme ?** + +User with this theme will continue to try to load this theme, without success if the theme was correctly deleted. It won't cause any malfunctions on the client side, and the default re2o theme (but not the default.css) theme will be loaded. Users will not be able to select this theme anymore afterwards. + +Try to not delete the default.css theme. \ No newline at end of file diff --git a/themes/cerulan.css b/themes/cerulan.css new file mode 100644 index 00000000..eb8bcf4c --- /dev/null +++ b/themes/cerulan.css @@ -0,0 +1,11 @@ +/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#2fa4e7;text-decoration:none}a:hover,a:focus{color:#157ab5;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.2;color:#317eac}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#2fa4e7}a.text-primary:hover,a.text-primary:focus{color:#178acc}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#2fa4e7}a.bg-primary:hover,a.bg-primary:focus{background-color:#178acc}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#555555;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#555555}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#959595}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#555555;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555555;background-color:#ffffff;border-color:rgba(0,0,0,0.1)}.btn-default:focus,.btn-default.focus{color:#555555;background-color:#e6e6e6;border-color:rgba(0,0,0,0.1)}.btn-default:hover{color:#555555;background-color:#e6e6e6;border-color:rgba(0,0,0,0.1)}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#555555;background-color:#e6e6e6;background-image:none;border-color:rgba(0,0,0,0.1)}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#555555;background-color:#d4d4d4;border-color:rgba(0,0,0,0.1)}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#ffffff;border-color:rgba(0,0,0,0.1)}.btn-default .badge{color:#ffffff;background-color:#555555}.btn-primary{color:#ffffff;background-color:#2fa4e7;border-color:#2fa4e7}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#178acc;border-color:#105b87}.btn-primary:hover{color:#ffffff;background-color:#178acc;border-color:#1684c2}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#178acc;background-image:none;border-color:#1684c2}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1474ac;border-color:#105b87}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2fa4e7;border-color:#2fa4e7}.btn-primary .badge{color:#2fa4e7;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#73a839;border-color:#73a839}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#59822c;border-color:#324919}.btn-success:hover{color:#ffffff;background-color:#59822c;border-color:#547a29}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#59822c;background-image:none;border-color:#547a29}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#476723;border-color:#324919}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#73a839;border-color:#73a839}.btn-success .badge{color:#73a839;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#033c73;border-color:#033c73}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#022241;border-color:#000000}.btn-info:hover{color:#ffffff;background-color:#022241;border-color:#011d37}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#022241;background-image:none;border-color:#011d37}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#01101f;border-color:#000000}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#033c73;border-color:#033c73}.btn-info .badge{color:#033c73;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#dd5600;border-color:#dd5600}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#aa4200;border-color:#5e2400}.btn-warning:hover{color:#ffffff;background-color:#aa4200;border-color:#a03e00}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#aa4200;background-image:none;border-color:#a03e00}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#863400;border-color:#5e2400}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#dd5600;border-color:#dd5600}.btn-warning .badge{color:#dd5600;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#c71c22;border-color:#c71c22}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#9a161a;border-color:#570c0f}.btn-danger:hover{color:#ffffff;background-color:#9a161a;border-color:#911419}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#9a161a;background-image:none;border-color:#911419}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#7b1115;border-color:#570c0f}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#c71c22;border-color:#c71c22}.btn-danger .badge{color:#c71c22;background-color:#ffffff}.btn-link{font-weight:400;color:#2fa4e7;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#157ab5;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#2fa4e7}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2fa4e7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#555555;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#2fa4e7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2fa4e7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#2fa4e7;border-color:#1995dc}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#dddddd}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#dddddd;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#178acc}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#178acc}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#dddddd;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#178acc}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#178acc}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#1995dc}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#dddddd}.navbar-inverse{background-color:#033c73;border-color:#022f5a}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#022f5a}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#022f5a}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#022f5a}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#022f5a}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#022a50}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#2fa4e7;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#157ab5;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#999999;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#2fa4e7}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#178acc}.label-success{background-color:#73a839}.label-success[href]:hover,.label-success[href]:focus{background-color:#59822c}.label-info{background-color:#033c73}.label-info[href]:hover,.label-info[href]:focus{background-color:#022241}.label-warning{background-color:#dd5600}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#aa4200}.label-danger{background-color:#c71c22}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#9a161a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#2fa4e7;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2fa4e7;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eeeeee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#2fa4e7}.thumbnail .caption{padding:9px;color:#555555}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#2fa4e7;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#73a839}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#033c73}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#dd5600}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#c71c22}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2fa4e7;border-color:#2fa4e7}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e6f4fc}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#555555;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#555555}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#dddddd}.panel-primary>.panel-heading{color:#ffffff;background-color:#2fa4e7;border-color:#dddddd}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-primary>.panel-heading .badge{color:#2fa4e7;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-success{border-color:#dddddd}.panel-success>.panel-heading{color:#468847;background-color:#73a839;border-color:#dddddd}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-success>.panel-heading .badge{color:#73a839;background-color:#468847}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-info{border-color:#dddddd}.panel-info>.panel-heading{color:#3a87ad;background-color:#033c73;border-color:#dddddd}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-info>.panel-heading .badge{color:#033c73;background-color:#3a87ad}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-warning{border-color:#dddddd}.panel-warning>.panel-heading{color:#c09853;background-color:#dd5600;border-color:#dddddd}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-warning>.panel-heading .badge{color:#dd5600;background-color:#c09853}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-danger{border-color:#dddddd}.panel-danger>.panel-heading{color:#b94a48;background-color:#c71c22;border-color:#dddddd}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-danger>.panel-heading .badge{color:#c71c22;background-color:#b94a48}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{background-image:linear-gradient(#54b4eb, #2fa4e7 60%, #1d9ce5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff54b4eb', endColorstr='#ff1d9ce5', GradientType=0);background-repeat:no-repeat;border-bottom:1px solid #178acc;-webkit-filter:none;filter:none;box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-default .badge{background-color:#fff;color:#2fa4e7}.navbar-inverse{background-image:linear-gradient(#04519b, #044687 60%, #033769);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff04519b', endColorstr='#ff033769', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #022241}.navbar-inverse .badge{background-color:#fff;color:#033c73}.navbar .navbar-nav>li>a,.navbar-brand{text-shadow:0 1px 0 rgba(0,0,0,0.1)}@media (max-width:767px){.navbar .dropdown-header{color:#fff}.navbar .dropdown-menu a{color:#fff}}.btn{text-shadow:0 1px 0 rgba(0,0,0,0.1)}.btn .caret{border-top-color:#fff}.btn-default{background-image:linear-gradient(#fff, #fff 60%, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff5f5f5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #e6e6e6}.btn-default:hover{color:#555555}.btn-default .caret{border-top-color:#555555}.btn-default{background-image:linear-gradient(#fff, #fff 60%, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff5f5f5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #e6e6e6}.btn-primary{background-image:linear-gradient(#54b4eb, #2fa4e7 60%, #1d9ce5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff54b4eb', endColorstr='#ff1d9ce5', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #178acc}.btn-success{background-image:linear-gradient(#88c149, #73a839 60%, #699934);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff88c149', endColorstr='#ff699934', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #59822c}.btn-info{background-image:linear-gradient(#04519b, #033c73 60%, #02325f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff04519b', endColorstr='#ff02325f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #022241}.btn-warning{background-image:linear-gradient(#ff6707, #dd5600 60%, #c94e00);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff6707', endColorstr='#ffc94e00', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #aa4200}.btn-danger{background-image:linear-gradient(#e12b31, #c71c22 60%, #b5191f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe12b31', endColorstr='#ffb5191f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-bottom:1px solid #9a161a}.panel-primary .panel-heading,.panel-success .panel-heading,.panel-warning .panel-heading,.panel-danger .panel-heading,.panel-info .panel-heading,.panel-primary .panel-title,.panel-success .panel-title,.panel-warning .panel-title,.panel-danger .panel-title,.panel-info .panel-title{color:#fff} \ No newline at end of file diff --git a/themes/cosmo.css b/themes/cosmo.css new file mode 100644 index 00000000..f27e7386 --- /dev/null +++ b/themes/cosmo.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Source Sans Pro",Calibri,Candara,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:#333333;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#2780e3;text-decoration:none}a:hover,a:focus{color:#165ba8;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:0}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #e6e6e6}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Source Sans Pro",Calibri,Candara,Arial,sans-serif;font-weight:300;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}mark,.mark{padding:.2em;background-color:#ff7518}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#2780e3}a.text-primary:hover,a.text-primary:focus{color:#1967be}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#2780e3}a.bg-primary:hover,a.bg-primary:focus{background-color:#1967be}.bg-success{background-color:#3fb618}a.bg-success:hover,a.bg-success:focus{background-color:#2f8912}.bg-info{background-color:#9954bb}a.bg-info:hover,a.bg-info:focus{background-color:#7e3f9d}.bg-warning{background-color:#ff7518}a.bg-warning:hover,a.bg-warning:focus{background-color:#e45c00}.bg-danger{background-color:#ff0039}a.bg-danger:hover,a.bg-danger:focus{background-color:#cc002e}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid #e6e6e6}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #e6e6e6}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #e6e6e6;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:0}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:0;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:0}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#3fb618}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#379f15}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#9954bb}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#8d46b0}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#ff7518}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#fe6600}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#ff0039}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#e60033}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:11px;font-size:15px;line-height:1.42857143;color:#333333}.form-control{display:block;width:100%;height:43px;padding:10px 18px;font-size:15px;line-height:1.42857143;color:#333333;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#e6e6e6;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:43px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:31px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:64px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:11px;padding-bottom:11px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:0}select.input-sm{height:31px;line-height:31px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:0}.form-group-sm select.form-control{height:31px;line-height:31px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:31px;min-height:34px;padding:6px 10px;font-size:13px;line-height:1.5}.input-lg{height:64px;padding:18px 30px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-lg{height:64px;line-height:64px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:64px;padding:18px 30px;font-size:19px;line-height:1.3333333;border-radius:0}.form-group-lg select.form-control{height:64px;line-height:64px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:64px;min-height:40px;padding:19px 30px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:53.75px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:43px;height:43px;line-height:43px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:64px;height:64px;line-height:64px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:31px;height:31px;line-height:31px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#3fb618;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#ff7518;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#ff0039;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:11px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:32px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:11px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:19px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:13px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:10px 18px;font-size:15px;line-height:1.42857143;border-radius:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#222222;border-color:#222222}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#090909;border-color:#000000}.btn-default:hover{color:#ffffff;background-color:#090909;border-color:#040404}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#090909;background-image:none;border-color:#040404}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#000000;border-color:#000000}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#222222;border-color:#222222}.btn-default .badge{color:#222222;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#2780e3;border-color:#2780e3}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#1967be;border-color:#10427b}.btn-primary:hover{color:#ffffff;background-color:#1967be;border-color:#1862b5}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#1967be;background-image:none;border-color:#1862b5}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#15569f;border-color:#10427b}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2780e3;border-color:#2780e3}.btn-primary .badge{color:#2780e3;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#3fb618;border-color:#3fb618}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#2f8912;border-color:#184509}.btn-success:hover{color:#ffffff;background-color:#2f8912;border-color:#2c8011}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#2f8912;background-image:none;border-color:#2c8011}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#24690e;border-color:#184509}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#3fb618;border-color:#3fb618}.btn-success .badge{color:#3fb618;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#9954bb;border-color:#9954bb}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#7e3f9d;border-color:#522967}.btn-info:hover{color:#ffffff;background-color:#7e3f9d;border-color:#783c96}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#7e3f9d;background-image:none;border-color:#783c96}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#6a3484;border-color:#522967}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#9954bb;border-color:#9954bb}.btn-info .badge{color:#9954bb;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#ff7518;border-color:#ff7518}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#e45c00;border-color:#983d00}.btn-warning:hover{color:#ffffff;background-color:#e45c00;border-color:#da5800}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#e45c00;background-image:none;border-color:#da5800}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#c04d00;border-color:#983d00}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#ff7518;border-color:#ff7518}.btn-warning .badge{color:#ff7518;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#ff0039;border-color:#ff0039}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#cc002e;border-color:#80001c}.btn-danger:hover{color:#ffffff;background-color:#cc002e;border-color:#c2002b}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#cc002e;background-image:none;border-color:#c2002b}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#a80026;border-color:#80001c}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#ff0039;border-color:#ff0039}.btn-danger .badge{color:#ff0039;background-color:#ffffff}.btn-link{font-weight:400;color:#2780e3;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#165ba8;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:18px 30px;font-size:19px;line-height:1.3333333;border-radius:0}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:13px;line-height:1.5;border-radius:0}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:0}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:0;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#2780e3}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2780e3;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:64px;padding:18px 30px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:64px;line-height:64px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:0}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:31px;line-height:31px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:10px 18px;font-size:15px;font-weight:400;line-height:1;color:#333333;text-align:center;background-color:#e6e6e6;border:1px solid #cccccc;border-radius:0}.input-group-addon.input-sm{padding:5px 10px;font-size:13px;border-radius:0}.input-group-addon.input-lg{padding:18px 30px;font-size:19px;border-radius:0}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#e6e6e6}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#e6e6e6;border-color:#2780e3}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:0 0 0 0}.nav-tabs>li>a:hover{border-color:#e6e6e6 #e6e6e6 #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2780e3}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:0}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:14.5px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:0}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.25px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:14.5px;padding-bottom:14.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:3.5px;margin-bottom:3.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:3.5px;margin-bottom:3.5px}.navbar-btn.btn-sm{margin-top:9.5px;margin-bottom:9.5px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:14.5px;margin-bottom:14.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#222222;border-color:#121212}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#090909}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#090909}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#090909}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#090909}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#090909}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#090909}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#121212}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#2780e3;border-color:#1967be}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#1967be}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#1967be}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#1967be}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#1967be}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#1967be}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#1967be}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#1967be}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ffffff;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#1967be}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#1a6ecc}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#ffffff}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#f5f5f5;border-radius:0}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:0}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:10px 18px;margin-left:-1px;line-height:1.42857143;color:#2780e3;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#165ba8;background-color:#e6e6e6;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#999999;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:18px 30px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:13px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:0}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#e6e6e6}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#222222}.label-default[href]:hover,.label-default[href]:focus{background-color:#090909}.label-primary{background-color:#2780e3}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1967be}.label-success{background-color:#3fb618}.label-success[href]:hover,.label-success[href]:focus{background-color:#2f8912}.label-info{background-color:#9954bb}.label-info[href]:hover,.label-info[href]:focus{background-color:#7e3f9d}.label-warning{background-color:#ff7518}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#e45c00}.label-danger{background-color:#ff0039}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#cc002e}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#2780e3;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2780e3;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#e6e6e6}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#cccccc}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:0}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#2780e3}.thumbnail .caption{padding:9px;color:#333333}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:0}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#3fb618;border-color:#4e9f15}.alert-success hr{border-top-color:#438912}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#9954bb;border-color:#7643a8}.alert-info hr{border-top-color:#693c96}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#ff7518;border-color:#ff4309}.alert-warning hr{border-top-color:#ee3800}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#ff0039;border-color:#f0005e}.alert-danger hr{border-top-color:#d60054}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#cccccc;border-radius:0;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:#2780e3;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#3fb618}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#9954bb}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#ff7518}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#ff0039}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#e6e6e6}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2780e3;border-color:#dddddd}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#dceafa}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#3fb618}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#379f15}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#9954bb}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#8d46b0}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#ff7518}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#fe6600}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#ff0039}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#e60033}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:0;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:-1;border-top-right-radius:-1}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:-1;border-top-right-radius:-1}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:-1}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:-1}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:0}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#2780e3}.panel-primary>.panel-heading{color:#ffffff;background-color:#2780e3;border-color:#2780e3}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2780e3}.panel-primary>.panel-heading .badge{color:#2780e3;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2780e3}.panel-success{border-color:#4e9f15}.panel-success>.panel-heading{color:#ffffff;background-color:#3fb618;border-color:#4e9f15}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#4e9f15}.panel-success>.panel-heading .badge{color:#3fb618;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#4e9f15}.panel-info{border-color:#7643a8}.panel-info>.panel-heading{color:#ffffff;background-color:#9954bb;border-color:#7643a8}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#7643a8}.panel-info>.panel-heading .badge{color:#9954bb;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#7643a8}.panel-warning{border-color:#ff4309}.panel-warning>.panel-heading{color:#ffffff;background-color:#ff7518;border-color:#ff4309}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ff4309}.panel-warning>.panel-heading .badge{color:#ff7518;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ff4309}.panel-danger{border-color:#f0005e}.panel-danger>.panel-heading{color:#ffffff;background-color:#ff0039;border-color:#f0005e}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f0005e}.panel-danger>.panel-heading .badge{color:#ff0039;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f0005e}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:0}.well-sm{padding:9px;border-radius:0}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid transparent;border-radius:0;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Source Sans Pro",Calibri,Candara,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:0}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Source Sans Pro",Calibri,Candara,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:0;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:-1 -1 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar-inverse .badge{background-color:#fff;color:#2780e3}body{-webkit-font-smoothing:antialiased}.text-primary,.text-primary:hover{color:#2780e3}.text-success,.text-success:hover{color:#3fb618}.text-danger,.text-danger:hover{color:#ff0039}.text-warning,.text-warning:hover{color:#ff7518}.text-info,.text-info:hover{color:#9954bb}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success a,.table .success a,table .warning a,.table .warning a,table .danger a,.table .danger a,table .info a,.table .info a{color:#fff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#ff7518}.has-warning .form-control,.has-warning .form-control:focus,.has-warning .input-group-addon{border:1px solid #ff7518}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#ff0039}.has-error .form-control,.has-error .form-control:focus,.has-error .input-group-addon{border:1px solid #ff0039}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#3fb618}.has-success .form-control,.has-success .form-control:focus,.has-success .input-group-addon{border:1px solid #3fb618}.nav-pills>li>a{border-radius:0}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:none}.close{text-decoration:none;text-shadow:none;opacity:0.4}.close:hover,.close:focus{opacity:1}.alert{border:none}.alert .alert-link{text-decoration:underline;color:#fff}.label{border-radius:0}.progress{height:8px;box-shadow:none}.progress .progress-bar{font-size:8px;line-height:8px}.panel-heading,.panel-footer{border-top-right-radius:0;border-top-left-radius:0}.panel-default .close{color:#333333}a.list-group-item-success.active{background-color:#3fb618}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#379f15}a.list-group-item-warning.active{background-color:#ff7518}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#fe6600}a.list-group-item-danger.active{background-color:#ff0039}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#e60033}.modal .close{color:#333333}.popover{color:#333333} diff --git a/themes/cyborg.css b/themes/cyborg.css new file mode 100644 index 00000000..ec8c5bd3 --- /dev/null +++ b/themes/cyborg.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#888888;background-color:#060606}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#2a9fd6;text-decoration:none}a:hover,a:focus{color:#2a9fd6;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#282828;border:1px solid #282828;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #282828}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:#ffffff}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#888888}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:56px}h2,.h2{font-size:45px}h3,.h3{font-size:34px}h4,.h4{font-size:24px}h5,.h5{font-size:20px}h6,.h6{font-size:16px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#ff8800}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#888888}.text-primary{color:#2a9fd6}a.text-primary:hover,a.text-primary:focus{color:#2180ac}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#2a9fd6}a.bg-primary:hover,a.bg-primary:focus{background-color:#2180ac}.bg-success{background-color:#77b300}a.bg-success:hover,a.bg-success:focus{background-color:#558000}.bg-info{background-color:#9933cc}a.bg-info:hover,a.bg-info:focus{background-color:#7a29a3}.bg-warning{background-color:#ff8800}a.bg-warning:hover,a.bg-warning:focus{background-color:#cc6d00}.bg-danger{background-color:#cc0000}a.bg-danger:hover,a.bg-danger:focus{background-color:#990000}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #282828}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #282828}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#555555}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #282828;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#282828;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:#181818}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#888888;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #282828}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #282828}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #282828}.table .table{background-color:#060606}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #282828}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #282828}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#080808}.table-hover>tbody>tr:hover{background-color:#282828}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#282828}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#1b1b1b}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#77b300}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#669a00}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#9933cc}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#8a2eb8}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#ff8800}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#e67a00}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#cc0000}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#b30000}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #282828}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#888888;border:0;border-bottom:1px solid #282828}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#888888}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#888888;background-color:#ffffff;background-image:none;border:1px solid #282828;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#888888;opacity:1}.form-control:-ms-input-placeholder{color:#888888}.form-control::-webkit-input-placeholder{color:#888888}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#adafae;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#77b300;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#ff8800;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#cc0000;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#c8c8c8}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#424242;border-color:#424242}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#282828;border-color:#020202}.btn-default:hover{color:#ffffff;background-color:#282828;border-color:#232323}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#282828;background-image:none;border-color:#232323}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#161616;border-color:#020202}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#424242;border-color:#424242}.btn-default .badge{color:#424242;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#2a9fd6;border-color:#2a9fd6}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#2180ac;border-color:#15506c}.btn-primary:hover{color:#ffffff;background-color:#2180ac;border-color:#1f79a3}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#2180ac;background-image:none;border-color:#1f79a3}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1b698e;border-color:#15506c}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2a9fd6;border-color:#2a9fd6}.btn-primary .badge{color:#2a9fd6;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#77b300;border-color:#77b300}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#558000;border-color:#223300}.btn-success:hover{color:#ffffff;background-color:#558000;border-color:#4e7600}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#558000;background-image:none;border-color:#4e7600}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#3d5c00;border-color:#223300}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#77b300;border-color:#77b300}.btn-success .badge{color:#77b300;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#9933cc;border-color:#9933cc}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#7a29a3;border-color:#4c1966}.btn-info:hover{color:#ffffff;background-color:#7a29a3;border-color:#74279b}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#7a29a3;background-image:none;border-color:#74279b}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#652287;border-color:#4c1966}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#9933cc;border-color:#9933cc}.btn-info .badge{color:#9933cc;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#ff8800;border-color:#ff8800}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#cc6d00;border-color:#804400}.btn-warning:hover{color:#ffffff;background-color:#cc6d00;border-color:#c26700}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#cc6d00;background-image:none;border-color:#c26700}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a85a00;border-color:#804400}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#ff8800;border-color:#ff8800}.btn-warning .badge{color:#ff8800;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#cc0000;border-color:#cc0000}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#990000;border-color:#4d0000}.btn-danger:hover{color:#ffffff;background-color:#990000;border-color:#8f0000}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#990000;background-image:none;border-color:#8f0000}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#750000;border-color:#4d0000}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#cc0000;border-color:#cc0000}.btn-danger .badge{color:#cc0000;background-color:#ffffff}.btn-link{font-weight:400;color:#2a9fd6;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a9fd6;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#888888;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#222222;background-clip:padding-box;border:1px solid #444444;border:1px solid rgba(255,255,255,0.1);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:rgba(255,255,255,0.1)}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#ffffff;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#2a9fd6}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2a9fd6;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#888888}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#888888;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#888888;text-align:center;background-color:#adafae;border:1px solid #282828;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#222222}.nav>li.disabled>a{color:#888888}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#888888;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#222222;border-color:#2a9fd6}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #282828}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:transparent transparent #282828}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#ffffff;cursor:default;background-color:#2a9fd6;border:1px solid #282828;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#060606}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2a9fd6}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#060606}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#060606;border-color:#282828}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-text{color:#888888}.navbar-default .navbar-nav>li>a{color:#888888}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#888888;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#888888}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#888888;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#282828}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#282828}.navbar-default .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#282828}.navbar-default .navbar-link{color:#888888}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#888888}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#888888}.navbar-inverse{background-color:#222222;border-color:#080808}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#888888}.navbar-inverse .navbar-nav>li>a{color:#888888}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#aaaaaa;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#888888}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#aaaaaa;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#888888}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#888888}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#aaaaaa}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#222222;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ffffff;content:"/\00a0"}.breadcrumb>.active{color:#888888}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#ffffff;text-decoration:none;background-color:#222222;border:1px solid #282828}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#2a9fd6;border-color:transparent}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#2a9fd6;border-color:transparent}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#888888;cursor:not-allowed;background-color:#222222;border-color:#282828}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#222222;border:1px solid #282828;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#2a9fd6}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#888888;cursor:not-allowed;background-color:#222222}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#424242}.label-default[href]:hover,.label-default[href]:focus{background-color:#282828}.label-primary{background-color:#2a9fd6}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#2180ac}.label-success{background-color:#77b300}.label-success[href]:hover,.label-success[href]:focus{background-color:#558000}.label-info{background-color:#9933cc}.label-info[href]:hover,.label-info[href]:focus{background-color:#7a29a3}.label-warning{background-color:#ff8800}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#cc6d00}.label-danger{background-color:#cc0000}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#990000}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#2a9fd6;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2a9fd6;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#151515}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#000000}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#282828;border:1px solid #282828;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#2a9fd6}.thumbnail .caption{padding:9px;color:#888888}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#77b300;border-color:#809a00}.alert-success hr{border-top-color:#6a8000}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#9933cc;border-color:#6e2caf}.alert-info hr{border-top-color:#61279b}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#ff8800;border-color:#f05800}.alert-warning hr{border-top-color:#d64f00}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#cc0000;border-color:#bd001f}.alert-danger hr{border-top-color:#a3001b}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#222222;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#2a9fd6;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#77b300}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#9933cc}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#ff8800}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#cc0000}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#222222;border:1px solid #282828}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#888888;cursor:not-allowed;background-color:#adafae}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#888888}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2a9fd6;border-color:#2a9fd6}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#d5ecf7}a.list-group-item,button.list-group-item{color:#888888}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#ffffff}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#888888;text-decoration:none;background-color:#484848}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#77b300}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#669a00}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#9933cc}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#8a2eb8}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#ff8800}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#e67a00}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#cc0000}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#b30000}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#222222;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#3c3c3c;border-top:1px solid #282828;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #282828}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #282828}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #282828}.panel-default{border-color:#282828}.panel-default>.panel-heading{color:#888888;background-color:#3c3c3c;border-color:#282828}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#282828}.panel-default>.panel-heading .badge{color:#3c3c3c;background-color:#888888}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#282828}.panel-primary{border-color:#2a9fd6}.panel-primary>.panel-heading{color:#ffffff;background-color:#2a9fd6;border-color:#2a9fd6}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2a9fd6}.panel-primary>.panel-heading .badge{color:#2a9fd6;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2a9fd6}.panel-success{border-color:#809a00}.panel-success>.panel-heading{color:#ffffff;background-color:#77b300;border-color:#809a00}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#809a00}.panel-success>.panel-heading .badge{color:#77b300;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#809a00}.panel-info{border-color:#6e2caf}.panel-info>.panel-heading{color:#ffffff;background-color:#9933cc;border-color:#6e2caf}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#6e2caf}.panel-info>.panel-heading .badge{color:#9933cc;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#6e2caf}.panel-warning{border-color:#f05800}.panel-warning>.panel-heading{color:#ffffff;background-color:#ff8800;border-color:#f05800}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f05800}.panel-warning>.panel-heading .badge{color:#ff8800;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f05800}.panel-danger{border-color:#bd001f}.panel-danger>.panel-heading{color:#ffffff;background-color:#cc0000;border-color:#bd001f}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bd001f}.panel-danger>.panel-heading .badge{color:#cc0000;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bd001f}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#151515;border:1px solid #030303;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#202020;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #282828}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #282828}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#202020;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#666666;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#202020;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#666666;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#202020;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#666666;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#202020}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#666666;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#202020}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#181818;border-bottom:1px solid #0b0b0b;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.text-primary,.text-primary:hover{color:#2a9fd6}.text-success,.text-success:hover{color:#77b300}.text-danger,.text-danger:hover{color:#cc0000}.text-warning,.text-warning:hover{color:#ff8800}.text-info,.text-info:hover{color:#9933cc}.bg-success,.bg-info,.bg-warning,.bg-danger{color:#fff}table,.table{color:#fff}table a:not(.btn),.table a:not(.btn){color:#fff;text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .text-muted,.table .text-muted{color:#888888}.table-responsive>.table{background-color:#181818}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#ff8800}.has-warning .form-control,.has-warning .form-control:focus,.has-warning .input-group-addon{border-color:#ff8800}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#cc0000}.has-error .form-control,.has-error .form-control:focus,.has-error .input-group-addon{border-color:#cc0000}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#77b300}.has-success .form-control,.has-success .form-control:focus,.has-success .input-group-addon{border-color:#77b300}legend{color:#fff}.input-group-addon{background-color:#424242}.nav-tabs a,.nav-pills a,.breadcrumb a,.pager a{color:#fff}.alert .alert-link,.alert a{color:#ffffff;text-decoration:underline}.alert .close{text-decoration:none}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#282828}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#282828}a.list-group-item-success.active{background-color:#77b300}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#669a00}a.list-group-item-warning.active{background-color:#ff8800}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#e67a00}a.list-group-item-danger.active{background-color:#cc0000}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#b30000}.jumbotron h1,.jumbotron h2,.jumbotron h3,.jumbotron h4,.jumbotron h5,.jumbotron h6{color:#fff} diff --git a/themes/darkly.css b/themes/darkly.css new file mode 100644 index 00000000..84d1651f --- /dev/null +++ b/themes/darkly.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:#ffffff;background-color:#222222}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#0ce3ac;text-decoration:none}a:hover,a:focus{color:#0ce3ac;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:2px;line-height:1.42857143;background-color:#222222;border:1px solid #464545;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #464545}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}mark,.mark{padding:.2em;background-color:#f39c12}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#375a7f}a.text-primary:hover,a.text-primary:focus{color:#28415b}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#375a7f}a.bg-primary:hover,a.bg-primary:focus{background-color:#28415b}.bg-success{background-color:#00bc8c}a.bg-success:hover,a.bg-success:focus{background-color:#008966}.bg-info{background-color:#3498db}a.bg-info:hover,a.bg-info:focus{background-color:#217dbb}.bg-warning{background-color:#f39c12}a.bg-warning:hover,a.bg-warning:focus{background-color:#c87f0a}.bg-danger{background-color:#e74c3c}a.bg-danger:hover,a.bg-danger:focus{background-color:#d62c1a}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid transparent}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #464545}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #464545;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;color:#303030;word-break:break-all;word-wrap:break-word;background-color:#ebebeb;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #464545}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #464545}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #464545}.table .table{background-color:#222222}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #464545}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #464545}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#3d3d3d}.table-hover>tbody>tr:hover{background-color:#464545}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#464545}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#393838}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#00bc8c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#00a379}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#3498db}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#258cd1}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f39c12}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#e08e0b}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#e74c3c}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#e43725}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #464545}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#ffffff;border:0;border-bottom:1px solid transparent}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:11px;font-size:15px;line-height:1.42857143;color:#464545}.form-control{display:block;width:100%;height:45px;padding:10px 15px;font-size:15px;line-height:1.42857143;color:#464545;background-color:#ffffff;background-image:none;border:1px solid #f1f1f1;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#ffffff;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(255,255,255,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#ebebeb;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:45px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:35px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:66px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:11px;padding-bottom:11px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-sm{height:35px;line-height:35px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:35px;line-height:35px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:35px;min-height:34px;padding:7px 9px;font-size:13px;line-height:1.5}.input-lg{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-lg{height:66px;line-height:66px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:66px;line-height:66px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:66px;min-height:40px;padding:19px 27px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:56.25px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:45px;height:45px;line-height:45px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:66px;height:66px;line-height:66px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:35px;height:35px;line-height:35px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#00bc8c;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#f39c12;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#e74c3c;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#ffffff}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:11px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:32px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:11px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:19px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:7px;font-size:13px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:10px 15px;font-size:15px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#464545;border-color:#464545}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#2c2c2c;border-color:#060606}.btn-default:hover{color:#ffffff;background-color:#2c2c2c;border-color:#272727}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#2c2c2c;background-image:none;border-color:#272727}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#1a1a1a;border-color:#060606}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#464545;border-color:#464545}.btn-default .badge{color:#464545;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#375a7f;border-color:#375a7f}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#28415b;border-color:#101b26}.btn-primary:hover{color:#ffffff;background-color:#28415b;border-color:#253c54}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#28415b;background-image:none;border-color:#253c54}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1d2f43;border-color:#101b26}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#375a7f;border-color:#375a7f}.btn-primary .badge{color:#375a7f;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#00bc8c;border-color:#00bc8c}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#008966;border-color:#003d2d}.btn-success:hover{color:#ffffff;background-color:#008966;border-color:#007f5e}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#008966;background-image:none;border-color:#007f5e}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#00654b;border-color:#003d2d}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#00bc8c;border-color:#00bc8c}.btn-success .badge{color:#00bc8c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#217dbb;border-color:#16527a}.btn-info:hover{color:#ffffff;background-color:#217dbb;border-color:#2077b2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#217dbb;background-image:none;border-color:#2077b2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#1c699d;border-color:#16527a}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#3498db;border-color:#3498db}.btn-info .badge{color:#3498db;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#c87f0a;border-color:#7f5006}.btn-warning:hover{color:#ffffff;background-color:#c87f0a;border-color:#be780a}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c87f0a;background-image:none;border-color:#be780a}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a66908;border-color:#7f5006}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f39c12;border-color:#f39c12}.btn-warning .badge{color:#f39c12;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#d62c1a;border-color:#921e12}.btn-danger:hover{color:#ffffff;background-color:#d62c1a;border-color:#cd2a19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d62c1a;background-image:none;border-color:#cd2a19}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#b62516;border-color:#921e12}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#e74c3c;border-color:#e74c3c}.btn-danger .badge{color:#e74c3c;background-color:#ffffff}.btn-link{font-weight:400;color:#0ce3ac;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#0ce3ac;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#303030;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#464545}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#ebebeb;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#375a7f}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#375a7f;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:66px;line-height:66px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:35px;line-height:35px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:10px 15px;font-size:15px;font-weight:400;line-height:1;color:#464545;text-align:center;background-color:#464545;border:1px solid transparent;border-radius:4px}.input-group-addon.input-sm{padding:6px 9px;font-size:13px;border-radius:3px}.input-group-addon.input-lg{padding:18px 27px;font-size:19px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#303030}.nav>li.disabled>a{color:#605e5e}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#605e5e;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#303030;border-color:#0ce3ac}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #464545}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#464545 #464545 #464545}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#00bc8c;cursor:default;background-color:#222222;border:1px solid #464545;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ebebeb}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#222222}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#375a7f}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ebebeb}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#222222}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:60px;padding:19.5px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:9.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19.5px;padding-bottom:19.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:7.5px;margin-bottom:7.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:7.5px;margin-bottom:7.5px}.navbar-btn.btn-sm{margin-top:12.5px;margin-bottom:12.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#375a7f;border-color:transparent}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#00bc8c;background-color:transparent}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#00bc8c;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#28415b}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#28415b}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#00bc8c;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#28415b}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#28415b}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#28415b}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#00bc8c}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#00bc8c}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#00bc8c;border-color:transparent}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#375a7f;background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#375a7f;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#00a379}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#aaaaaa;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#00a379}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#375a7f;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#00a379}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#aaaaaa;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#008966}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#008966}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#009871}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#375a7f}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#375a7f}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#aaaaaa}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#464545;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ffffff;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:10px 15px;margin-left:-1px;line-height:1.42857143;color:#ffffff;text-decoration:none;background-color:#00bc8c;border:1px solid transparent}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#00dba3;border-color:transparent}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#00dba3;border-color:transparent}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#ffffff;cursor:not-allowed;background-color:#007053;border-color:transparent}.pagination-lg>li>a,.pagination-lg>li>span{padding:18px 27px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:6px 9px;font-size:13px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#00bc8c;border:1px solid transparent;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#00dba3}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#dddddd;cursor:not-allowed;background-color:#00bc8c}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#464545}.label-default[href]:hover,.label-default[href]:focus{background-color:#2c2c2c}.label-primary{background-color:#375a7f}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#28415b}.label-success{background-color:#00bc8c}.label-success[href]:hover,.label-success[href]:focus{background-color:#008966}.label-info{background-color:#3498db}.label-info[href]:hover,.label-info[href]:focus{background-color:#217dbb}.label-warning{background-color:#f39c12}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c87f0a}.label-danger{background-color:#e74c3c}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d62c1a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#464545;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#375a7f;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#303030}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#161616}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:2px;margin-bottom:21px;line-height:1.42857143;background-color:#222222;border:1px solid #464545;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#0ce3ac}.thumbnail .caption{padding:9px;color:#ffffff}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#00bc8c;border-color:#00bc8c}.alert-success hr{border-top-color:#00a379}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.alert-info hr{border-top-color:#258cd1}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.alert-warning hr{border-top-color:#e08e0b}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.alert-danger hr{border-top-color:#e43725}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#ebebeb;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:#375a7f;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#00bc8c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#3498db}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#e74c3c}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#303030;border:1px solid #464545}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#ebebeb}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#375a7f;border-color:#375a7f}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#a8c0da}a.list-group-item,button.list-group-item{color:#0ce3ac}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#0bcb9a}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#0ce3ac;text-decoration:none;background-color:transparent}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#00bc8c}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#00a379}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#3498db}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#258cd1}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f39c12}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#e08e0b}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#e74c3c}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#e43725}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#303030;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#464545;border-top:1px solid #464545;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #464545}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #464545}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #464545}.panel-default{border-color:#464545}.panel-default>.panel-heading{color:#ffffff;background-color:#303030;border-color:#464545}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#464545}.panel-default>.panel-heading .badge{color:#303030;background-color:#ffffff}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#464545}.panel-primary{border-color:#375a7f}.panel-primary>.panel-heading{color:#ffffff;background-color:#375a7f;border-color:#375a7f}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#375a7f}.panel-primary>.panel-heading .badge{color:#375a7f;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#375a7f}.panel-success{border-color:#00bc8c}.panel-success>.panel-heading{color:#ffffff;background-color:#00bc8c;border-color:#00bc8c}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#00bc8c}.panel-success>.panel-heading .badge{color:#00bc8c;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#00bc8c}.panel-info{border-color:#3498db}.panel-info>.panel-heading{color:#ffffff;background-color:#3498db;border-color:#3498db}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3498db}.panel-info>.panel-heading .badge{color:#3498db;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3498db}.panel-warning{border-color:#f39c12}.panel-warning>.panel-heading{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f39c12}.panel-warning>.panel-heading .badge{color:#f39c12;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f39c12}.panel-danger{border-color:#e74c3c}.panel-danger>.panel-heading{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e74c3c}.panel-danger>.panel-heading .badge{color:#e74c3c;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e74c3c}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#303030;border:1px solid transparent;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:none;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#303030;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=70);opacity:0.7}.modal-header{padding:15px;border-bottom:1px solid #464545}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #464545}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#303030;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#666666;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#303030;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#666666;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#303030;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#666666;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#303030}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#666666;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#303030}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#282828;border-bottom:1px solid #1c1c1c;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0}.navbar-default .badge{background-color:#fff;color:#375a7f}.navbar-inverse .badge{background-color:#fff;color:#00bc8c}.navbar-brand{line-height:1}.navbar-form .form-control{background-color:white}.navbar-form .form-control:focus{border-color:white}.btn{border-width:2px}.btn:active{box-shadow:none}.btn-group.open .dropdown-toggle{box-shadow:none}.text-primary,.text-primary:hover{color:#4673a3}.text-success,.text-success:hover{color:#00bc8c}.text-danger,.text-danger:hover{color:#e74c3c}.text-warning,.text-warning:hover{color:#f39c12}.text-info,.text-info:hover{color:#3498db}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success>th>a,.table .success>th>a,table .warning>th>a,.table .warning>th>a,table .danger>th>a,.table .danger>th>a,table .info>th>a,.table .info>th>a,table .success>td>a,.table .success>td>a,table .warning>td>a,.table .warning>td>a,table .danger>td>a,.table .danger>td>a,table .info>td>a,.table .info>td>a,table .success>a,.table .success>a,table .warning>a,.table .warning>a,table .danger>a,.table .danger>a,table .info>a,.table .info>a{color:#fff}table>thead>tr>th,.table>thead>tr>th,table>tbody>tr>th,.table>tbody>tr>th,table>tfoot>tr>th,.table>tfoot>tr>th,table>thead>tr>td,.table>thead>tr>td,table>tbody>tr>td,.table>tbody>tr>td,table>tfoot>tr>td,.table>tfoot>tr>td{border:none}table-bordered>thead>tr>th,.table-bordered>thead>tr>th,table-bordered>tbody>tr>th,.table-bordered>tbody>tr>th,table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>th,table-bordered>thead>tr>td,.table-bordered>thead>tr>td,table-bordered>tbody>tr>td,.table-bordered>tbody>tr>td,table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #464545}input,textarea{color:#464545}.form-control,input,textarea{border:2px hidden transparent;box-shadow:none}.form-control:focus,input:focus,textarea:focus{box-shadow:none}.form-control-feedback{color:#464545}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f39c12}.has-warning .form-control,.has-warning .form-control:focus{box-shadow:none}.has-warning .input-group-addon{border-color:#f39c12}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#e74c3c}.has-error .form-control,.has-error .form-control:focus{box-shadow:none}.has-error .input-group-addon{border-color:#e74c3c}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#00bc8c}.has-success .form-control,.has-success .form-control:focus{box-shadow:none}.has-success .input-group-addon{border-color:#00bc8c}.input-group-addon{color:#ffffff}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:#464545}.nav-tabs>li>a,.nav-pills>li>a{color:#fff}.pager a,.pager a:hover{color:#fff}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{background-color:#007053}.breadcrumb a{color:#fff}.close{text-decoration:none;text-shadow:none;opacity:0.4}.close:hover,.close:focus{opacity:1}.alert .alert-link{color:#fff;text-decoration:underline}.progress{height:10px;box-shadow:none}.progress .progress-bar{font-size:10px;line-height:10px}.well{box-shadow:none}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#464545}a.list-group-item-success.active{background-color:#00bc8c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#00a379}a.list-group-item-warning.active{background-color:#f39c12}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#e08e0b}a.list-group-item-danger.active{background-color:#e74c3c}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#e43725}.popover{color:#ffffff}.panel-default>.panel-heading{background-color:#464545} diff --git a/themes/default.css b/themes/default.css new file mode 100644 index 00000000..e69de29b diff --git a/themes/flatly.css b/themes/flatly.css new file mode 100644 index 00000000..2f615143 --- /dev/null +++ b/themes/flatly.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Lato:400,700,400italic&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:#2c3e50;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#18bc9c;text-decoration:none}a:hover,a:focus{color:#18bc9c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #ecf0f1}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#b4bcc2}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}mark,.mark{padding:.2em;background-color:#f39c12}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#b4bcc2}.text-primary{color:#2c3e50}a.text-primary:hover,a.text-primary:focus{color:#1a242f}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#2c3e50}a.bg-primary:hover,a.bg-primary:focus{background-color:#1a242f}.bg-success{background-color:#18bc9c}a.bg-success:hover,a.bg-success:focus{background-color:#128f76}.bg-info{background-color:#3498db}a.bg-info:hover,a.bg-info:focus{background-color:#217dbb}.bg-warning{background-color:#f39c12}a.bg-warning:hover,a.bg-warning:focus{background-color:#c87f0a}.bg-danger{background-color:#e74c3c}a.bg-danger:hover,a.bg-danger:focus{background-color:#d62c1a}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid transparent}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #ecf0f1}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#b4bcc2}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #ecf0f1;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;color:#7b8a8b;word-break:break-all;word-wrap:break-word;background-color:#ecf0f1;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#b4bcc2;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ecf0f1}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ecf0f1}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ecf0f1}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#ecf0f1}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#ecf0f1}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#dde4e6}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#18bc9c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#15a589}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#3498db}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#258cd1}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f39c12}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#e08e0b}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#e74c3c}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#e43725}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ecf0f1}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#2c3e50;border:0;border-bottom:1px solid transparent}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:11px;font-size:15px;line-height:1.42857143;color:#2c3e50}.form-control{display:block;width:100%;height:45px;padding:10px 15px;font-size:15px;line-height:1.42857143;color:#2c3e50;background-color:#ffffff;background-image:none;border:1px solid #dce4ec;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#2c3e50;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(44,62,80,0.6)}.form-control::-moz-placeholder{color:#acb6c0;opacity:1}.form-control:-ms-input-placeholder{color:#acb6c0}.form-control::-webkit-input-placeholder{color:#acb6c0}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#ecf0f1;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:45px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:35px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:66px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:11px;padding-bottom:11px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-sm{height:35px;line-height:35px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:35px;line-height:35px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:35px;min-height:34px;padding:7px 9px;font-size:13px;line-height:1.5}.input-lg{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-lg{height:66px;line-height:66px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:66px;line-height:66px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:66px;min-height:40px;padding:19px 27px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:56.25px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:45px;height:45px;line-height:45px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:66px;height:66px;line-height:66px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:35px;height:35px;line-height:35px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#18bc9c;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#f39c12;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#e74c3c;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#597ea2}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:11px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:32px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:11px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:19px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:7px;font-size:13px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:10px 15px;font-size:15px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#95a5a6;border-color:#95a5a6}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#798d8f;border-color:#566566}.btn-default:hover{color:#ffffff;background-color:#798d8f;border-color:#74898a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#798d8f;background-image:none;border-color:#74898a}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#687b7c;border-color:#566566}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#95a5a6;border-color:#95a5a6}.btn-default .badge{color:#95a5a6;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#1a242f;border-color:#000000}.btn-primary:hover{color:#ffffff;background-color:#1a242f;border-color:#161f29}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#1a242f;background-image:none;border-color:#161f29}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#0d1318;border-color:#000000}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2c3e50;border-color:#2c3e50}.btn-primary .badge{color:#2c3e50;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#18bc9c;border-color:#18bc9c}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#128f76;border-color:#0a4b3e}.btn-success:hover{color:#ffffff;background-color:#128f76;border-color:#11866f}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#128f76;background-image:none;border-color:#11866f}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#0e6f5c;border-color:#0a4b3e}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#18bc9c;border-color:#18bc9c}.btn-success .badge{color:#18bc9c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#217dbb;border-color:#16527a}.btn-info:hover{color:#ffffff;background-color:#217dbb;border-color:#2077b2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#217dbb;background-image:none;border-color:#2077b2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#1c699d;border-color:#16527a}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#3498db;border-color:#3498db}.btn-info .badge{color:#3498db;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#c87f0a;border-color:#7f5006}.btn-warning:hover{color:#ffffff;background-color:#c87f0a;border-color:#be780a}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c87f0a;background-image:none;border-color:#be780a}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a66908;border-color:#7f5006}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f39c12;border-color:#f39c12}.btn-warning .badge{color:#f39c12;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#d62c1a;border-color:#921e12}.btn-danger:hover{color:#ffffff;background-color:#d62c1a;border-color:#cd2a19}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d62c1a;background-image:none;border-color:#cd2a19}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#b62516;border-color:#921e12}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#e74c3c;border-color:#e74c3c}.btn-danger .badge{color:#e74c3c;background-color:#ffffff}.btn-link{font-weight:400;color:#18bc9c;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#18bc9c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#b4bcc2;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#7b8a8b;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#2c3e50}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2c3e50;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#b4bcc2}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#b4bcc2;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:66px;padding:18px 27px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:66px;line-height:66px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:35px;padding:6px 9px;font-size:13px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:35px;line-height:35px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:10px 15px;font-size:15px;font-weight:400;line-height:1;color:#2c3e50;text-align:center;background-color:#ecf0f1;border:1px solid #dce4ec;border-radius:4px}.input-group-addon.input-sm{padding:6px 9px;font-size:13px;border-radius:3px}.input-group-addon.input-lg{padding:18px 27px;font-size:19px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#ecf0f1}.nav>li.disabled>a{color:#b4bcc2}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#b4bcc2;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#ecf0f1;border-color:#18bc9c}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ecf0f1}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#ecf0f1 #ecf0f1 #ecf0f1}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#2c3e50;cursor:default;background-color:#ffffff;border:1px solid #ecf0f1;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2c3e50}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ecf0f1}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ecf0f1;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:60px;padding:19.5px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:9.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19.5px;padding-bottom:19.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:7.5px;margin-bottom:7.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:7.5px;margin-bottom:7.5px}.navbar-btn.btn-sm{margin-top:12.5px;margin-bottom:12.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#2c3e50;border-color:transparent}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#1a242f}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#18bc9c;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#1a242f}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#1a242f}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#1a242f}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#18bc9c}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#18bc9c}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#18bc9c;border-color:transparent}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#15a589}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#2c3e50;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#15a589}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#128f76}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#128f76}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#149c82}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#2c3e50}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#2c3e50}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#ecf0f1;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#95a5a6}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:10px 15px;margin-left:-1px;line-height:1.42857143;color:#ffffff;text-decoration:none;background-color:#18bc9c;border:1px solid transparent}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#0f7864;border-color:transparent}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#0f7864;border-color:transparent}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#ecf0f1;cursor:not-allowed;background-color:#3be6c4;border-color:transparent}.pagination-lg>li>a,.pagination-lg>li>span{padding:18px 27px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:6px 9px;font-size:13px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#18bc9c;border:1px solid transparent;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#0f7864}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#ffffff;cursor:not-allowed;background-color:#18bc9c}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#95a5a6}.label-default[href]:hover,.label-default[href]:focus{background-color:#798d8f}.label-primary{background-color:#2c3e50}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1a242f}.label-success{background-color:#18bc9c}.label-success[href]:hover,.label-success[href]:focus{background-color:#128f76}.label-info{background-color:#3498db}.label-info[href]:hover,.label-info[href]:focus{background-color:#217dbb}.label-warning{background-color:#f39c12}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c87f0a}.label-danger{background-color:#e74c3c}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d62c1a}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#2c3e50;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2c3e50;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#ecf0f1}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#cfd9db}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#ffffff;border:1px solid #ecf0f1;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#18bc9c}.thumbnail .caption{padding:9px;color:#2c3e50}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#18bc9c;border-color:#18bc9c}.alert-success hr{border-top-color:#15a589}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#3498db;border-color:#3498db}.alert-info hr{border-top-color:#258cd1}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.alert-warning hr{border-top-color:#e08e0b}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.alert-danger hr{border-top-color:#e43725}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#ecf0f1;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:#2c3e50;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#18bc9c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#3498db}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f39c12}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#e74c3c}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #ecf0f1}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#b4bcc2;cursor:not-allowed;background-color:#ecf0f1}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#b4bcc2}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#8aa4be}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#ecf0f1}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#18bc9c}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#15a589}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#3498db}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#258cd1}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f39c12}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#e08e0b}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#e74c3c}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#e43725}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#ecf0f1;border-top:1px solid #ecf0f1;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ecf0f1}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #ecf0f1}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ecf0f1}.panel-default{border-color:#ecf0f1}.panel-default>.panel-heading{color:#2c3e50;background-color:#ecf0f1;border-color:#ecf0f1}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ecf0f1}.panel-default>.panel-heading .badge{color:#ecf0f1;background-color:#2c3e50}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ecf0f1}.panel-primary{border-color:#2c3e50}.panel-primary>.panel-heading{color:#ffffff;background-color:#2c3e50;border-color:#2c3e50}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2c3e50}.panel-primary>.panel-heading .badge{color:#2c3e50;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2c3e50}.panel-success{border-color:#18bc9c}.panel-success>.panel-heading{color:#ffffff;background-color:#18bc9c;border-color:#18bc9c}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#18bc9c}.panel-success>.panel-heading .badge{color:#18bc9c;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#18bc9c}.panel-info{border-color:#3498db}.panel-info>.panel-heading{color:#ffffff;background-color:#3498db;border-color:#3498db}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3498db}.panel-info>.panel-heading .badge{color:#3498db;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3498db}.panel-warning{border-color:#f39c12}.panel-warning>.panel-heading{color:#ffffff;background-color:#f39c12;border-color:#f39c12}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f39c12}.panel-warning>.panel-heading .badge{color:#f39c12;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f39c12}.panel-danger{border-color:#e74c3c}.panel-danger>.panel-heading{color:#ffffff;background-color:#e74c3c;border-color:#e74c3c}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e74c3c}.panel-danger>.panel-heading .badge{color:#e74c3c;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e74c3c}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#ecf0f1;border:1px solid transparent;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#000000;text-shadow:none;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0}.navbar-default .badge{background-color:#fff;color:#2c3e50}.navbar-inverse .badge{background-color:#fff;color:#18bc9c}.navbar-brand{line-height:1}.btn{border-width:2px}.btn:active{box-shadow:none}.btn-group.open .dropdown-toggle{box-shadow:none}.text-primary,.text-primary:hover{color:#2c3e50}.text-success,.text-success:hover{color:#18bc9c}.text-danger,.text-danger:hover{color:#e74c3c}.text-warning,.text-warning:hover{color:#f39c12}.text-info,.text-info:hover{color:#3498db}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success>th>a,.table .success>th>a,table .warning>th>a,.table .warning>th>a,table .danger>th>a,.table .danger>th>a,table .info>th>a,.table .info>th>a,table .success>td>a,.table .success>td>a,table .warning>td>a,.table .warning>td>a,table .danger>td>a,.table .danger>td>a,table .info>td>a,.table .info>td>a,table .success>a,.table .success>a,table .warning>a,.table .warning>a,table .danger>a,.table .danger>a,table .info>a,.table .info>a{color:#fff}table>thead>tr>th,.table>thead>tr>th,table>tbody>tr>th,.table>tbody>tr>th,table>tfoot>tr>th,.table>tfoot>tr>th,table>thead>tr>td,.table>thead>tr>td,table>tbody>tr>td,.table>tbody>tr>td,table>tfoot>tr>td,.table>tfoot>tr>td{border:none}table-bordered>thead>tr>th,.table-bordered>thead>tr>th,table-bordered>tbody>tr>th,.table-bordered>tbody>tr>th,table-bordered>tfoot>tr>th,.table-bordered>tfoot>tr>th,table-bordered>thead>tr>td,.table-bordered>thead>tr>td,table-bordered>tbody>tr>td,.table-bordered>tbody>tr>td,table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ecf0f1}.form-control,input{border-width:2px;box-shadow:none}.form-control:focus,input:focus{box-shadow:none}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f39c12}.has-warning .form-control,.has-warning .form-control:focus{border:2px solid #f39c12}.has-warning .input-group-addon{border-color:#f39c12}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#e74c3c}.has-error .form-control,.has-error .form-control:focus{border:2px solid #e74c3c}.has-error .input-group-addon{border-color:#e74c3c}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#18bc9c}.has-success .form-control,.has-success .form-control:focus{border:2px solid #18bc9c}.has-success .input-group-addon{border-color:#18bc9c}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.pager a,.pager a:hover{color:#fff}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{background-color:#3be6c4}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.alert .alert-link{color:#fff;text-decoration:underline}.progress{height:10px;box-shadow:none}.progress .progress-bar{font-size:10px;line-height:10px}.well{box-shadow:none}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#ecf0f1}a.list-group-item-success.active{background-color:#18bc9c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#15a589}a.list-group-item-warning.active{background-color:#f39c12}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#e08e0b}a.list-group-item-danger.active{background-color:#e74c3c}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#e43725}.panel-default .close{color:#2c3e50}.modal .close{color:#2c3e50}.popover{color:#2c3e50} diff --git a/themes/journal.css b/themes/journal.css new file mode 100644 index 00000000..d2b38d93 --- /dev/null +++ b/themes/journal.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=News+Cycle:400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:Georgia,"Times New Roman",Times,serif;font-size:15px;line-height:1.42857143;color:#777777;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#eb6864;text-decoration:none}a:hover,a:focus{color:#e22620;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"News Cycle","Arial Narrow Bold",sans-serif;font-weight:700;line-height:1.1;color:#000000}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:86%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#eb6864}a.text-primary:hover,a.text-primary:focus{color:#e53c37}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#eb6864}a.bg-primary:hover,a.bg-primary:focus{background-color:#e53c37}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#777777;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:15px;line-height:1.42857143;color:#777777}.form-control{display:block;width:100%;height:39px;padding:8px 12px;font-size:15px;line-height:1.42857143;color:#777777;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:39px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:31px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:56px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:3px}select.input-sm{height:31px;line-height:31px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:31px;line-height:31px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:31px;min-height:34px;padding:6px 10px;font-size:13px;line-height:1.5}.input-lg{height:56px;padding:14px 16px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-lg{height:56px;line-height:56px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:56px;padding:14px 16px;font-size:19px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:56px;line-height:56px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:56px;min-height:40px;padding:15px 16px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:48.75px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:39px;height:39px;line-height:39px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:56px;height:56px;line-height:56px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:31px;height:31px;line-height:31px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#b7b7b7}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:30px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:13px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:15px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#999999;border-color:#999999}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#808080;border-color:#595959}.btn-default:hover{color:#ffffff;background-color:#808080;border-color:#7a7a7a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#808080;background-image:none;border-color:#7a7a7a}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#6e6e6e;border-color:#595959}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#999999;border-color:#999999}.btn-default .badge{color:#999999;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#eb6864;border-color:#eb6864}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#e53c37;border-color:#b81c18}.btn-primary:hover{color:#ffffff;background-color:#e53c37;border-color:#e4332e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#e53c37;background-image:none;border-color:#e4332e}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#dc221c;border-color:#b81c18}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#eb6864;border-color:#eb6864}.btn-primary .badge{color:#eb6864;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#22b24c;border-color:#22b24c}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#1a873a;border-color:#0e471e}.btn-success:hover{color:#ffffff;background-color:#1a873a;border-color:#187f36}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#1a873a;background-image:none;border-color:#187f36}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#14692d;border-color:#0e471e}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#22b24c;border-color:#22b24c}.btn-success .badge{color:#22b24c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#336699;border-color:#336699}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#264c73;border-color:#132639}.btn-info:hover{color:#ffffff;background-color:#264c73;border-color:#24476b}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#264c73;background-image:none;border-color:#24476b}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#1d3b58;border-color:#132639}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#336699;border-color:#336699}.btn-info .badge{color:#336699;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f5e625;border-color:#f5e625}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#ddce0a;border-color:#948a07}.btn-warning:hover{color:#ffffff;background-color:#ddce0a;border-color:#d3c50a}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#ddce0a;background-image:none;border-color:#d3c50a}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#bbae09;border-color:#948a07}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f5e625;border-color:#f5e625}.btn-warning .badge{color:#f5e625;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#f57a00;border-color:#f57a00}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#c26100;border-color:#763b00}.btn-danger:hover{color:#ffffff;background-color:#c26100;border-color:#b85c00}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#c26100;background-image:none;border-color:#b85c00}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#9e4f00;border-color:#763b00}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#f57a00;border-color:#f57a00}.btn-danger .badge{color:#f57a00;background-color:#ffffff}.btn-link{font-weight:400;color:#eb6864;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#e22620;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:19px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:13px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:13px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#eb6864}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#eb6864;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:13px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:56px;padding:14px 16px;font-size:19px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:56px;line-height:56px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:31px;padding:5px 10px;font-size:13px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:31px;line-height:31px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:15px;font-weight:400;line-height:1;color:#777777;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:13px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:19px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#eb6864}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#777777;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#eb6864}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:60px;padding:19.5px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:9.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:19.5px;padding-bottom:19.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:10.5px;margin-bottom:10.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:10.5px;margin-bottom:10.5px}.navbar-btn.btn-sm{margin-top:14.5px;margin-bottom:14.5px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:19.5px;margin-bottom:19.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#ffffff;border-color:#eeeeee}.navbar-default .navbar-brand{color:#000000}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#000000;background-color:#eeeeee}.navbar-default .navbar-text{color:#000000}.navbar-default .navbar-nav>li>a{color:#000000}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#000000;background-color:#eeeeee}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#000000;background-color:#eeeeee}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#000000;background-color:#eeeeee}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#000000}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#000000;background-color:#eeeeee}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#000000;background-color:#eeeeee}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#dddddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#dddddd}.navbar-default .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#eeeeee}.navbar-default .navbar-link{color:#000000}.navbar-default .navbar-link:hover{color:#000000}.navbar-default .btn-link{color:#000000}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#000000}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#eb6864;border-color:#e53c37}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:#e74b47}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#e74b47}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#e74b47}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#e74b47}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#e53c37}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#e53c37}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#e74b47}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#e74b47}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#e53c37}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#e53c37}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#e74944}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444444}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#eb6864;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#e22620;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#999999;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:13px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#eb6864}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#e53c37}.label-success{background-color:#22b24c}.label-success[href]:hover,.label-success[href]:focus{background-color:#1a873a}.label-info{background-color:#336699}.label-info[href]:hover,.label-info[href]:focus{background-color:#264c73}.label-warning{background-color:#f5e625}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ddce0a}.label-danger{background-color:#f57a00}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c26100}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:13px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#eb6864;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#eb6864;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eeeeee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#eb6864}.thumbnail .caption{padding:9px;color:#777777}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:13px;line-height:21px;color:#ffffff;text-align:center;background-color:#eb6864;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#22b24c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#336699}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f5e625}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#f57a00}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#eb6864;border-color:#eb6864}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#ffffff}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#777777;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#777777}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#eb6864}.panel-primary>.panel-heading{color:#ffffff;background-color:#eb6864;border-color:#eb6864}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#eb6864}.panel-primary>.panel-heading .badge{color:#eb6864;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#eb6864}.panel-success{border-color:#22b24c}.panel-success>.panel-heading{color:#468847;background-color:#22b24c;border-color:#22b24c}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#22b24c}.panel-success>.panel-heading .badge{color:#22b24c;background-color:#468847}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#22b24c}.panel-info{border-color:#336699}.panel-info>.panel-heading{color:#3a87ad;background-color:#336699;border-color:#336699}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#336699}.panel-info>.panel-heading .badge{color:#336699;background-color:#3a87ad}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#336699}.panel-warning{border-color:#f5e625}.panel-warning>.panel-heading{color:#c09853;background-color:#f5e625;border-color:#f5e625}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f5e625}.panel-warning>.panel-heading .badge{color:#f5e625;background-color:#c09853}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f5e625}.panel-danger{border-color:#f57a00}.panel-danger>.panel-heading{color:#b94a48;background-color:#f57a00;border-color:#f57a00}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f57a00}.panel-danger>.panel-heading .badge{color:#f57a00;background-color:#b94a48}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f57a00}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:Georgia,"Times New Roman",Times,serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:Georgia,"Times New Roman",Times,serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{font-size:18px;font-family:"News Cycle","Arial Narrow Bold",sans-serif;font-weight:700}.navbar-default .badge{background-color:#000;color:#fff}.navbar-inverse .badge{background-color:#fff;color:#eb6864}.navbar-brand{font-size:inherit;font-weight:700;text-transform:uppercase}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f57a00}.has-warning .form-control,.has-warning .form-control:focus{border-color:#f57a00}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#eb6864}.has-error .form-control,.has-error .form-control:focus{border-color:#eb6864}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#22b24c}.has-success .form-control,.has-success .form-control:focus{border-color:#22b24c}.badge{padding-bottom:4px;vertical-align:3px;font-size:10px}.jumbotron h1,.jumbotron h2,.jumbotron h3,.jumbotron h4,.jumbotron h5,.jumbotron h6{font-family:"News Cycle","Arial Narrow Bold",sans-serif;font-weight:700;color:#000}.panel-primary .panel-title,.panel-success .panel-title,.panel-warning .panel-title,.panel-danger .panel-title,.panel-info .panel-title{color:#fff} diff --git a/themes/lumen.css b/themes/lumen.css new file mode 100644 index 00000000..68aedbde --- /dev/null +++ b/themes/lumen.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700,400italic&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#158cba;text-decoration:none}a:hover,a:focus{color:#158cba;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:5px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #eeeeee;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:400;line-height:1.1;color:#333333}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#ff851b}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#158cba}a.text-primary:hover,a.text-primary:focus{color:#106a8c}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#158cba}a.bg-primary:hover,a.bg-primary:focus{background-color:#106a8c}.bg-success{background-color:#28b62c}a.bg-success:hover,a.bg-success:focus{background-color:#1f8c22}.bg-info{background-color:#75caeb}a.bg-info:hover,a.bg-info:focus{background-color:#48b9e5}.bg-warning{background-color:#ff851b}a.bg-warning:hover,a.bg-warning:focus{background-color:#e76b00}.bg-danger{background-color:#ff4136}a.bg-danger:hover,a.bg-danger:focus{background-color:#ff1103}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:2px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #eeeeee}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #eeeeee}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #eeeeee}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #eeeeee}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #eeeeee}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#28b62c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#23a127}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#75caeb}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#5fc1e8}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#ff851b}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#ff7701}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#ff4136}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ff291c}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #eeeeee}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:8px;font-size:14px;line-height:1.42857143;color:#555555}.form-control{display:block;width:100%;height:38px;padding:7px 12px;font-size:14px;line-height:1.42857143;color:#555555;background-color:#ffffff;background-image:none;border:1px solid #e7e7e7;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:28px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:52px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:8px;padding-bottom:8px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-sm{height:28px;line-height:28px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.form-group-sm select.form-control{height:28px;line-height:28px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:28px;min-height:32px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-lg{height:52px;line-height:52px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.form-group-lg select.form-control{height:52px;line-height:52px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:52px;min-height:38px;padding:14px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:52px;height:52px;line-height:52px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:28px;height:28px;line-height:28px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#28b62c;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#ff851b;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#ff4136;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#959595}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:8px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:28px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:8px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:5px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:7px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#555555;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#555555;background-color:#eeeeee;border-color:#e2e2e2}.btn-default:focus,.btn-default.focus{color:#555555;background-color:#d5d5d5;border-color:#a2a2a2}.btn-default:hover{color:#555555;background-color:#d5d5d5;border-color:#c3c3c3}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#555555;background-color:#d5d5d5;background-image:none;border-color:#c3c3c3}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#555555;background-color:#c3c3c3;border-color:#a2a2a2}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#eeeeee;border-color:#e2e2e2}.btn-default .badge{color:#eeeeee;background-color:#555555}.btn-primary{color:#ffffff;background-color:#158cba;border-color:#127ba3}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#106a8c;border-color:#052531}.btn-primary:hover{color:#ffffff;background-color:#106a8c;border-color:#0c516c}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#106a8c;background-image:none;border-color:#0c516c}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#0c516c;border-color:#052531}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#158cba;border-color:#127ba3}.btn-primary .badge{color:#158cba;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#28b62c;border-color:#23a127}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#1f8c22;border-color:#0c390e}.btn-success:hover{color:#ffffff;background-color:#1f8c22;border-color:#186f1b}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#1f8c22;background-image:none;border-color:#186f1b}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#186f1b;border-color:#0c390e}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#28b62c;border-color:#23a127}.btn-success .badge{color:#28b62c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#75caeb;border-color:#5fc1e8}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#48b9e5;border-color:#1984ae}.btn-info:hover{color:#ffffff;background-color:#48b9e5;border-color:#29ade0}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#48b9e5;background-image:none;border-color:#29ade0}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#29ade0;border-color:#1984ae}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#75caeb;border-color:#5fc1e8}.btn-info .badge{color:#75caeb;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#ff851b;border-color:#ff7701}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#e76b00;border-color:#813c00}.btn-warning:hover{color:#ffffff;background-color:#e76b00;border-color:#c35b00}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#e76b00;background-image:none;border-color:#c35b00}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#c35b00;border-color:#813c00}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#ff851b;border-color:#ff7701}.btn-warning .badge{color:#ff851b;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#ff4136;border-color:#ff291c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#ff1103;border-color:#9c0900}.btn-danger:hover{color:#ffffff;background-color:#ff1103;border-color:#de0c00}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#ff1103;background-image:none;border-color:#de0c00}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#de0c00;border-color:#9c0900}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#ff4136;border-color:#ff291c}.btn-danger .badge{color:#ff4136;background-color:#ffffff}.btn-link{font-weight:400;color:#158cba;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#158cba;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}.btn-sm,.btn-group-sm>.btn{padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:2px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid #e7e7e7;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#eeeeee}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#333333;text-decoration:none;background-color:transparent}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#158cba;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#eeeeee}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:52px;padding:13px 16px;font-size:18px;line-height:1.3333333;border-radius:5px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:52px;line-height:52px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:28px;padding:4px 10px;font-size:12px;line-height:1.5;border-radius:2px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:28px;line-height:28px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:7px 12px;font-size:14px;font-weight:400;line-height:1;color:#555555;text-align:center;background-color:#eeeeee;border:1px solid #e7e7e7;border-radius:4px}.input-group-addon.input-sm{padding:4px 10px;font-size:12px;border-radius:2px}.input-group-addon.input-lg{padding:13px 16px;font-size:18px;border-radius:5px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#ffffff}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#ffffff;border-color:#158cba}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #e7e7e7}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #e7e7e7}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #e7e7e7;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#158cba}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #e7e7e7}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #e7e7e7;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:11px;margin-bottom:11px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#333333}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#333333;background-color:transparent}.navbar-default .navbar-text{color:#555555}.navbar-default .navbar-nav>li>a{color:#999999}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#eeeeee;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#333333;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#999999}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#eeeeee;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#eeeeee}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ffffff}.navbar-default .navbar-toggle .icon-bar{background-color:#999999}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#999999}.navbar-default .navbar-link:hover{color:#333333}.navbar-default .btn-link{color:#999999}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#333333}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#eeeeee}.navbar-inverse{background-color:#ffffff;border-color:#e6e6e6}.navbar-inverse .navbar-brand{color:#999999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-text{color:#999999}.navbar-inverse .navbar-nav>li>a{color:#999999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#eeeeee;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#333333;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#e6e6e6}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#eeeeee;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#eeeeee}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#eeeeee}.navbar-inverse .navbar-toggle .icon-bar{background-color:#999999}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#ededed}.navbar-inverse .navbar-link{color:#999999}.navbar-inverse .navbar-link:hover{color:#333333}.navbar-inverse .btn-link{color:#999999}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#333333}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#eeeeee}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#fafafa;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#999999;content:">\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:7px 12px;margin-left:-1px;line-height:1.42857143;color:#555555;text-decoration:none;background-color:#eeeeee;border:1px solid #e2e2e2}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#555555;background-color:#eeeeee;border-color:#e2e2e2}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#158cba;border-color:#127ba3}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee;border-color:#e2e2e2}.pagination-lg>li>a,.pagination-lg>li>span{padding:13px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:5px;border-bottom-left-radius:5px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:5px;border-bottom-right-radius:5px}.pagination-sm>li>a,.pagination-sm>li>span{padding:4px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:2px;border-bottom-left-radius:2px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#eeeeee;border:1px solid #e2e2e2;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#eeeeee}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#158cba}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#106a8c}.label-success{background-color:#28b62c}.label-success[href]:hover,.label-success[href]:focus{background-color:#1f8c22}.label-info{background-color:#75caeb}.label-info[href]:hover,.label-info[href]:focus{background-color:#48b9e5}.label-warning{background-color:#ff851b}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#e76b00}.label-danger{background-color:#ff4136}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#ff1103}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:normal;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#158cba;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#158cba;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#fafafa}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#e1e1e1}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:5px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #eeeeee;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#158cba}.thumbnail .caption{padding:9px;color:#555555}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#28b62c;border-color:#24a528}.alert-success hr{border-top-color:#209023}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#75caeb;border-color:#40b5e3}.alert-info hr{border-top-color:#29ade0}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#ff851b;border-color:#ff7701}.alert-warning hr{border-top-color:#e76b00}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#ff4136;border-color:#ff1103}.alert-danger hr{border-top-color:#e90d00}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#fafafa;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#158cba;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#28b62c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#75caeb}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#ff851b}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#ff4136}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #eeeeee}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#158cba;border-color:#158cba}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#a6dff5}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#28b62c}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#23a127}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#75caeb}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#5fc1e8}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#ff851b}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#ff7701}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#ff4136}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#ff291c}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid transparent;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #eeeeee}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid transparent}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid transparent}.panel-default{border-color:transparent}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:transparent}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-primary{border-color:transparent}.panel-primary>.panel-heading{color:#ffffff;background-color:#158cba;border-color:transparent}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-primary>.panel-heading .badge{color:#158cba;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-success{border-color:transparent}.panel-success>.panel-heading{color:#ffffff;background-color:#28b62c;border-color:transparent}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-success>.panel-heading .badge{color:#28b62c;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-info{border-color:transparent}.panel-info>.panel-heading{color:#ffffff;background-color:#75caeb;border-color:transparent}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-info>.panel-heading .badge{color:#75caeb;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-warning{border-color:transparent}.panel-warning>.panel-heading{color:#ffffff;background-color:#ff851b;border-color:transparent}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-warning>.panel-heading .badge{color:#ff851b;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-danger{border-color:transparent}.panel-danger>.panel-heading{color:#ffffff;background-color:#ff4136;border-color:transparent}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-danger>.panel-heading .badge{color:#ff4136;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#fafafa;border:1px solid #e8e8e8;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:5px}.well-sm{padding:9px;border-radius:2px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #eeeeee;border:1px solid rgba(0,0,0,0.05);border-radius:5px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:5px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:4px 4px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border-width:0 1px 4px 1px}.btn{padding:9px 12px 7px;border-width:0 1px 4px 1px;font-size:12px;font-weight:bold;text-transform:uppercase}.btn:hover{margin-top:1px;border-bottom-width:3px}.btn:active{margin-top:2px;border-bottom-width:2px;box-shadow:none}.btn-lg,.btn-group-lg>.btn{padding:15px 16px 13px;line-height:15px}.btn-sm,.btn-group-sm>.btn{padding:6px 10px 4px}.btn-xs,.btn-group-xs>.btn{padding:3px 5px 1px}.btn-default:hover,.btn-default:focus,.btn-group.open .dropdown-toggle.btn-default{background-color:#eeeeee;border-color:#e2e2e2}.btn-primary:hover,.btn-primary:focus,.btn-group.open .dropdown-toggle.btn-primary{background-color:#158cba;border-color:#127ba3}.btn-success:hover,.btn-success:focus,.btn-group.open .dropdown-toggle.btn-success{background-color:#28b62c;border-color:#23a127}.btn-info:hover,.btn-info:focus,.btn-group.open .dropdown-toggle.btn-info{background-color:#75caeb;border-color:#5fc1e8}.btn-warning:hover,.btn-warning:focus,.btn-group.open .dropdown-toggle.btn-warning{background-color:#ff851b;border-color:#ff7701}.btn-danger:hover,.btn-danger:focus,.btn-group.open .dropdown-toggle.btn-danger{background-color:#ff4136;border-color:#ff291c}.btn-group.open .dropdown-toggle{box-shadow:none}.navbar-btn:hover{margin-top:8px}.navbar-btn:active{margin-top:9px}.navbar-btn.btn-sm:hover{margin-top:11px}.navbar-btn.btn-sm:active{margin-top:12px}.navbar-btn.btn-xs:hover{margin-top:15px}.navbar-btn.btn-xs:active{margin-top:16px}.btn-group-vertical .btn+.btn:hover{border-top-width:1px}.btn-group-vertical .btn+.btn:active{border-top-width:2px}.text-primary,.text-primary:hover{color:#158cba}.text-success,.text-success:hover{color:#28b62c}.text-danger,.text-danger:hover{color:#ff4136}.text-warning,.text-warning:hover{color:#ff851b}.text-info,.text-info:hover{color:#75caeb}table a:not(.btn),.table a:not(.btn){text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .success,.table .success,table .warning,.table .warning,table .danger,.table .danger,table .info,.table .info{color:#fff}table .success a:not(.btn),.table .success a:not(.btn),table .warning a:not(.btn),.table .warning a:not(.btn),table .danger a:not(.btn),.table .danger a:not(.btn),table .info a:not(.btn),.table .info a:not(.btn){color:#fff}table:not(.table-bordered)>thead>tr>th,.table:not(.table-bordered)>thead>tr>th,table:not(.table-bordered)>tbody>tr>th,.table:not(.table-bordered)>tbody>tr>th,table:not(.table-bordered)>tfoot>tr>th,.table:not(.table-bordered)>tfoot>tr>th,table:not(.table-bordered)>thead>tr>td,.table:not(.table-bordered)>thead>tr>td,table:not(.table-bordered)>tbody>tr>td,.table:not(.table-bordered)>tbody>tr>td,table:not(.table-bordered)>tfoot>tr>td,.table:not(.table-bordered)>tfoot>tr>td{border-color:transparent}.form-control{box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}label{font-weight:normal}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#ff851b}.has-warning .form-control,.has-warning .form-control:focus{border:1px solid #ff851b;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-warning .input-group-addon{border:1px solid #ff851b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#ff4136}.has-error .form-control,.has-error .form-control:focus{border:1px solid #ff4136;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-error .input-group-addon{border:1px solid #ff4136}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#28b62c}.has-success .form-control,.has-success .form-control:focus{border:1px solid #28b62c;box-shadow:inset 0 2px 0 rgba(0,0,0,0.075)}.has-success .input-group-addon{border:1px solid #28b62c}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.nav-tabs>li>a{margin-top:6px;border-color:#e7e7e7;color:#333333;transition:all .2s ease-in-out}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus,.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus,.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{padding-bottom:16px;margin-top:0}.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{border-color:#e7e7e7}.nav-tabs>li.disabled>a:hover,.nav-tabs>li.disabled>a:focus{padding-top:10px;padding-bottom:10px;margin-top:6px}.nav-tabs.nav-justified>li{vertical-align:bottom}.dropdown-menu{margin-top:0;border-width:0 1px 4px 1px;border-top-width:1px;box-shadow:none}.breadcrumb{border-color:#ededed;border-style:solid;border-width:0 1px 4px 1px}.pagination>li>a,.pager>li>a,.pagination>li>span,.pager>li>span{position:relative;top:0;border-width:0 1px 4px 1px;color:#555555;font-size:12px;font-weight:bold;text-transform:uppercase}.pagination>li>a:hover,.pager>li>a:hover,.pagination>li>span:hover,.pager>li>span:hover{top:1px;border-bottom-width:3px}.pagination>li>a:active,.pager>li>a:active,.pagination>li>span:active,.pager>li>span:active{top:2px;border-bottom-width:2px}.pagination>.disabled>a:hover,.pager>.disabled>a:hover,.pagination>.disabled>span:hover,.pager>.disabled>span:hover{top:0;border-width:0 1px 4px 1px}.pagination>.disabled>a:active,.pager>.disabled>a:active,.pagination>.disabled>span:active,.pager>.disabled>span:active{top:0;border-width:0 1px 4px 1px}.pager>li>a,.pager>li>span,.pager>.disabled>a,.pager>.disabled>span,.pager>li>a:hover,.pager>li>span:hover,.pager>.disabled>a:hover,.pager>.disabled>span:hover,.pager>li>a:active,.pager>li>span:active,.pager>.disabled>a:active,.pager>.disabled>span:active{border-left-width:2px;border-right-width:2px}.close{color:#fff;text-decoration:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.alert{border-width:0 1px 4px 1px}.alert .alert-link{font-weight:normal;color:#fff;text-decoration:underline}.label{font-weight:normal}.progress{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.1)}.progress-bar{box-shadow:inset 0 -4px 0 rgba(0,0,0,0.15)}.well{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.05)}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:#eeeeee}a.list-group-item-success.active{background-color:#28b62c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#23a127}a.list-group-item-warning.active{background-color:#ff851b}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#ff7701}a.list-group-item-danger.active{background-color:#ff4136}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#ff291c}.jumbotron{border:1px solid #e7e7e7;box-shadow:inset 0 2px 0 rgba(0,0,0,0.05)}.panel{border:1px solid #e7e7e7;border-width:0 1px 4px 1px}.panel-default .close{color:#555555}.modal .close{color:#555555}.popover{color:#555555} diff --git a/themes/paper.css b/themes/paper.css new file mode 100644 index 00000000..fc97524a --- /dev/null +++ b/themes/paper.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:1.846;color:#666666;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#2196f3;text-decoration:none}a:hover,a:focus{color:#0a6ebd;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:3px}.img-thumbnail{padding:4px;line-height:1.846;background-color:#ffffff;border:1px solid #dddddd;border-radius:3px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:23px;margin-bottom:23px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:400;line-height:1.1;color:#444444}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#bbbbbb}h1,.h1,h2,.h2,h3,.h3{margin-top:23px;margin-bottom:11.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:11.5px;margin-bottom:11.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:56px}h2,.h2{font-size:45px}h3,.h3{font-size:34px}h4,.h4{font-size:24px}h5,.h5{font-size:20px}h6,.h6{font-size:14px}p{margin:0 0 11.5px}.lead{margin-bottom:23px;font-size:14px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:19.5px}}small,.small{font-size:92%}mark,.mark{padding:.2em;background-color:#ffe0b2}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#bbbbbb}.text-primary{color:#2196f3}a.text-primary:hover,a.text-primary:focus{color:#0c7cd5}.text-success{color:#4caf50}a.text-success:hover,a.text-success:focus{color:#3d8b40}.text-info{color:#9c27b0}a.text-info:hover,a.text-info:focus{color:#771e86}.text-warning{color:#ff9800}a.text-warning:hover,a.text-warning:focus{color:#cc7a00}.text-danger{color:#e51c23}a.text-danger:hover,a.text-danger:focus{color:#b9151b}.bg-primary{color:#fff;background-color:#2196f3}a.bg-primary:hover,a.bg-primary:focus{background-color:#0c7cd5}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#e1bee7}a.bg-info:hover,a.bg-info:focus{background-color:#d099d9}.bg-warning{background-color:#ffe0b2}a.bg-warning:hover,a.bg-warning:focus{background-color:#ffcb7f}.bg-danger{background-color:#f9bdbb}a.bg-danger:hover,a.bg-danger:focus{background-color:#f5908c}.page-header{padding-bottom:10.5px;margin:46px 0 23px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:11.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:23px}dt,dd{line-height:1.846}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:11.5px 23px;margin:0 0 23px;font-size:16.25px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.846;color:#bbbbbb}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:23px;font-style:normal;line-height:1.846}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:3px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:11px;margin:0 0 11.5px;font-size:12px;line-height:1.846;color:#212121;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:3px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#bbbbbb;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:23px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.846;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#e1bee7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#d8abe0}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#ffe0b2}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#ffd699}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f9bdbb}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#f7a6a4}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:17.25px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:23px;font-size:19.5px;line-height:inherit;color:#212121;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:13px;line-height:1.846;color:#666666}.form-control{display:block;width:100%;height:37px;padding:6px 16px;font-size:13px;line-height:1.846;color:#666666;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:3px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#bbbbbb;opacity:1}.form-control:-ms-input-placeholder{color:#bbbbbb}.form-control::-webkit-input-placeholder{color:#bbbbbb}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:transparent;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:37px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:45px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:23px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:35px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:45px;padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:3px}select.input-lg{height:45px;line-height:45px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:45px;padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:3px}.form-group-lg select.form-control{height:45px;line-height:45px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:45px;min-height:40px;padding:11px 16px;font-size:17px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:46.25px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:37px;height:37px;line-height:37px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:45px;height:45px;line-height:45px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#4caf50}.has-success .form-control{border-color:#4caf50;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#3d8b40;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #92cf94}.has-success .input-group-addon{color:#4caf50;background-color:#dff0d8;border-color:#4caf50}.has-success .form-control-feedback{color:#4caf50}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ff9800}.has-warning .form-control{border-color:#ff9800;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#cc7a00;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ffc166}.has-warning .input-group-addon{color:#ff9800;background-color:#ffe0b2;border-color:#ff9800}.has-warning .form-control-feedback{color:#ff9800}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#e51c23}.has-error .form-control{border-color:#e51c23;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#b9151b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ef787c}.has-error .input-group-addon{color:#e51c23;background-color:#f9bdbb;border-color:#e51c23}.has-error .form-control-feedback{color:#e51c23}.has-feedback label~.form-control-feedback{top:28px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#a6a6a6}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:30px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:17px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 16px;font-size:13px;line-height:1.846;border-radius:3px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#444444;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#444444;background-color:#ffffff;border-color:transparent}.btn-default:focus,.btn-default.focus{color:#444444;background-color:#e6e6e6;border-color:rgba(0,0,0,0)}.btn-default:hover{color:#444444;background-color:#e6e6e6;border-color:rgba(0,0,0,0)}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#444444;background-color:#e6e6e6;background-image:none;border-color:rgba(0,0,0,0)}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#444444;background-color:#d4d4d4;border-color:rgba(0,0,0,0)}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#ffffff;border-color:transparent}.btn-default .badge{color:#ffffff;background-color:#444444}.btn-primary{color:#ffffff;background-color:#2196f3;border-color:transparent}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#0c7cd5;border-color:rgba(0,0,0,0)}.btn-primary:hover{color:#ffffff;background-color:#0c7cd5;border-color:rgba(0,0,0,0)}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#0c7cd5;background-image:none;border-color:rgba(0,0,0,0)}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#0a68b4;border-color:rgba(0,0,0,0)}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#2196f3;border-color:transparent}.btn-primary .badge{color:#2196f3;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#4caf50;border-color:transparent}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#3d8b40;border-color:rgba(0,0,0,0)}.btn-success:hover{color:#ffffff;background-color:#3d8b40;border-color:rgba(0,0,0,0)}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#3d8b40;background-image:none;border-color:rgba(0,0,0,0)}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#327334;border-color:rgba(0,0,0,0)}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#4caf50;border-color:transparent}.btn-success .badge{color:#4caf50;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#9c27b0;border-color:transparent}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#771e86;border-color:rgba(0,0,0,0)}.btn-info:hover{color:#ffffff;background-color:#771e86;border-color:rgba(0,0,0,0)}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#771e86;background-image:none;border-color:rgba(0,0,0,0)}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#5d1769;border-color:rgba(0,0,0,0)}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#9c27b0;border-color:transparent}.btn-info .badge{color:#9c27b0;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#ff9800;border-color:transparent}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#cc7a00;border-color:rgba(0,0,0,0)}.btn-warning:hover{color:#ffffff;background-color:#cc7a00;border-color:rgba(0,0,0,0)}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#cc7a00;background-image:none;border-color:rgba(0,0,0,0)}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a86400;border-color:rgba(0,0,0,0)}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#ff9800;border-color:transparent}.btn-warning .badge{color:#ff9800;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#e51c23;border-color:transparent}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#b9151b;border-color:rgba(0,0,0,0)}.btn-danger:hover{color:#ffffff;background-color:#b9151b;border-color:rgba(0,0,0,0)}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#b9151b;background-image:none;border-color:rgba(0,0,0,0)}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#991216;border-color:rgba(0,0,0,0)}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#e51c23;border-color:transparent}.btn-danger .badge{color:#e51c23;background-color:#ffffff}.btn-link{font-weight:400;color:#2196f3;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#0a6ebd;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#bbbbbb;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:3px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:13px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:10.5px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.846;color:#666666;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#141414;text-decoration:none;background-color:#eeeeee}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#2196f3;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#bbbbbb}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.846;color:#bbbbbb;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:45px;padding:10px 16px;font-size:17px;line-height:1.3333333;border-radius:3px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:45px;line-height:45px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 16px;font-size:13px;font-weight:400;line-height:1;color:#666666;text-align:center;background-color:transparent;border:1px solid transparent;border-radius:3px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:17px;border-radius:3px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#bbbbbb}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#bbbbbb;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#2196f3}.nav .nav-divider{height:1px;margin:10.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid transparent}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.846;border:1px solid transparent;border-radius:3px 3px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee transparent}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#666666;cursor:default;background-color:transparent;border:1px solid transparent;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:3px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid transparent}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid transparent;border-radius:3px 3px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:3px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#2196f3}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:3px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid transparent}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid transparent;border-radius:3px 3px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:64px;margin-bottom:23px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:3px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:64px;padding:20.5px 15px;font-size:17px;line-height:23px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:15px;margin-bottom:15px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:3px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:10.25px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:23px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:23px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:20.5px;padding-bottom:20.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:13.5px;margin-bottom:13.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:3px;border-top-right-radius:3px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:13.5px;margin-bottom:13.5px}.navbar-btn.btn-sm{margin-top:17px;margin-bottom:17px}.navbar-btn.btn-xs{margin-top:21px;margin-bottom:21px}.navbar-text{margin-top:20.5px;margin-bottom:20.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#ffffff;border-color:transparent}.navbar-default .navbar-brand{color:#666666}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#212121;background-color:transparent}.navbar-default .navbar-text{color:#bbbbbb}.navbar-default .navbar-nav>li>a{color:#666666}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#212121;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#212121;background-color:#eeeeee}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#212121;background-color:#eeeeee}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#666666}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#212121;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#212121;background-color:#eeeeee}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:transparent}.navbar-default .navbar-toggle .icon-bar{background-color:rgba(0,0,0,0.5)}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-link{color:#666666}.navbar-default .navbar-link:hover{color:#212121}.navbar-default .btn-link{color:#666666}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#212121}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#2196f3;border-color:transparent}.navbar-inverse .navbar-brand{color:#b2dbfb}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#bbbbbb}.navbar-inverse .navbar-nav>li>a{color:#b2dbfb}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#0c7cd5}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#0c7cd5}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#b2dbfb}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#0c7cd5}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:transparent}.navbar-inverse .navbar-toggle .icon-bar{background-color:rgba(0,0,0,0.5)}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#0c84e4}.navbar-inverse .navbar-link{color:#b2dbfb}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#b2dbfb}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444444}.breadcrumb{padding:8px 15px;margin-bottom:23px;list-style:none;background-color:#f5f5f5;border-radius:3px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#bbbbbb}.pagination{display:inline-block;padding-left:0;margin:23px 0;border-radius:3px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 16px;margin-left:-1px;line-height:1.846;color:#2196f3;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#0a6ebd;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#2196f3;border-color:#2196f3}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#bbbbbb;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:17px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:23px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#bbbbbb;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#bbbbbb}.label-default[href]:hover,.label-default[href]:focus{background-color:#a2a2a2}.label-primary{background-color:#2196f3}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#0c7cd5}.label-success{background-color:#4caf50}.label-success[href]:hover,.label-success[href]:focus{background-color:#3d8b40}.label-info{background-color:#9c27b0}.label-info[href]:hover,.label-info[href]:focus{background-color:#771e86}.label-warning{background-color:#ff9800}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#cc7a00}.label-danger{background-color:#e51c23}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#b9151b}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:normal;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#bbbbbb;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#2196f3;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#f9f9f9}.jumbotron h1,.jumbotron .h1{color:#444444}.jumbotron p{margin-bottom:15px;font-size:20px;font-weight:200}.jumbotron>hr{border-top-color:#e0e0e0}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:3px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:59px}}.thumbnail{display:block;padding:4px;margin-bottom:23px;line-height:1.846;background-color:#ffffff;border:1px solid #dddddd;border-radius:3px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#2196f3}.thumbnail .caption{padding:9px;color:#666666}.alert{padding:15px;margin-bottom:23px;border:1px solid transparent;border-radius:3px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#4caf50;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#3d8b40}.alert-info{color:#9c27b0;background-color:#e1bee7;border-color:#cba4dd}.alert-info hr{border-top-color:#c191d6}.alert-info .alert-link{color:#771e86}.alert-warning{color:#ff9800;background-color:#ffe0b2;border-color:#ffc599}.alert-warning hr{border-top-color:#ffb67f}.alert-warning .alert-link{color:#cc7a00}.alert-danger{color:#e51c23;background-color:#f9bdbb;border-color:#f7a4af}.alert-danger hr{border-top-color:#f58c9a}.alert-danger .alert-link{color:#b9151b}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:23px;margin-bottom:23px;overflow:hidden;background-color:#f5f5f5;border-radius:3px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:23px;color:#ffffff;text-align:center;background-color:#2196f3;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#4caf50}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#9c27b0}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#ff9800}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#e51c23}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#bbbbbb;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#bbbbbb}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#2196f3;border-color:#2196f3}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#e3f2fd}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#4caf50;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#4caf50}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#4caf50;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#4caf50;border-color:#4caf50}.list-group-item-info{color:#9c27b0;background-color:#e1bee7}a.list-group-item-info,button.list-group-item-info{color:#9c27b0}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#9c27b0;background-color:#d8abe0}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#9c27b0;border-color:#9c27b0}.list-group-item-warning{color:#ff9800;background-color:#ffe0b2}a.list-group-item-warning,button.list-group-item-warning{color:#ff9800}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ff9800;background-color:#ffd699}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ff9800;border-color:#ff9800}.list-group-item-danger{color:#e51c23;background-color:#f9bdbb}a.list-group-item-danger,button.list-group-item-danger{color:#e51c23}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#e51c23;background-color:#f7a6a4}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#e51c23;border-color:#e51c23}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:23px;background-color:#ffffff;border:1px solid transparent;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:2px;border-top-right-radius:2px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:15px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:2px;border-top-right-radius:2px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:2px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:2px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:2px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:2px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:23px}.panel-group .panel{margin-bottom:0;border-radius:3px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#212121;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#212121}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#2196f3}.panel-primary>.panel-heading{color:#ffffff;background-color:#2196f3;border-color:#2196f3}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#2196f3}.panel-primary>.panel-heading .badge{color:#2196f3;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#2196f3}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#ffffff;background-color:#4caf50;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#4caf50;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#cba4dd}.panel-info>.panel-heading{color:#ffffff;background-color:#9c27b0;border-color:#cba4dd}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#cba4dd}.panel-info>.panel-heading .badge{color:#9c27b0;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#cba4dd}.panel-warning{border-color:#ffc599}.panel-warning>.panel-heading{color:#ffffff;background-color:#ff9800;border-color:#ffc599}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ffc599}.panel-warning>.panel-heading .badge{color:#ff9800;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ffc599}.panel-danger{border-color:#f7a4af}.panel-danger>.panel-heading{color:#ffffff;background-color:#e51c23;border-color:#f7a4af}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f7a4af}.panel-danger>.panel-heading .badge{color:#e51c23;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f7a4af}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f9f9f9;border:1px solid transparent;border-radius:3px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:3px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:19.5px;font-weight:normal;line-height:1;color:#000000;text-shadow:none;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid transparent;border-radius:3px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid transparent}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.846}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid transparent}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.846;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#727272}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#727272}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#727272}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#727272}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#727272}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#727272}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#727272}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#727272}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#727272;border-radius:3px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.846;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;background-color:#ffffff;background-clip:padding-box;border:1px solid transparent;border-radius:3px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:rgba(0,0,0,0);border-top-color:rgba(0,0,0,0.075);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:rgba(0,0,0,0);border-right-color:rgba(0,0,0,0.075);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:rgba(0,0,0,0);border-bottom-color:rgba(0,0,0,0.075)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:rgba(0,0,0,0);border-left-color:rgba(0,0,0,0.075)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:13px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:2px 2px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border:none;box-shadow:0 1px 2px rgba(0,0,0,0.3)}.navbar-brand{font-size:24px}.navbar-inverse .navbar-form input[type=text],.navbar-inverse .navbar-form input[type=password]{color:#fff;box-shadow:inset 0 -1px 0 #b2dbfb}.navbar-inverse .navbar-form input[type=text]::-moz-placeholder,.navbar-inverse .navbar-form input[type=password]::-moz-placeholder{color:#b2dbfb;opacity:1}.navbar-inverse .navbar-form input[type=text]:-ms-input-placeholder,.navbar-inverse .navbar-form input[type=password]:-ms-input-placeholder{color:#b2dbfb}.navbar-inverse .navbar-form input[type=text]::-webkit-input-placeholder,.navbar-inverse .navbar-form input[type=password]::-webkit-input-placeholder{color:#b2dbfb}.navbar-inverse .navbar-form input[type=text]:focus,.navbar-inverse .navbar-form input[type=password]:focus{box-shadow:inset 0 -2px 0 #fff}.btn-default{background-size:200% 200%;background-position:50%}.btn-default:focus{background-color:#ffffff}.btn-default:hover,.btn-default:active:hover{background-color:#f0f0f0}.btn-default:active{background-color:#e0e0e0;background-image:radial-gradient(circle, #e0e0e0 10%, #fff 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-primary{background-size:200% 200%;background-position:50%}.btn-primary:focus{background-color:#2196f3}.btn-primary:hover,.btn-primary:active:hover{background-color:#0d87e9}.btn-primary:active{background-color:#0b76cc;background-image:radial-gradient(circle, #0b76cc 10%, #2196f3 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-success{background-size:200% 200%;background-position:50%}.btn-success:focus{background-color:#4caf50}.btn-success:hover,.btn-success:active:hover{background-color:#439a46}.btn-success:active{background-color:#39843c;background-image:radial-gradient(circle, #39843c 10%, #4caf50 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-info{background-size:200% 200%;background-position:50%}.btn-info:focus{background-color:#9c27b0}.btn-info:hover,.btn-info:active:hover{background-color:#862197}.btn-info:active{background-color:#701c7e;background-image:radial-gradient(circle, #701c7e 10%, #9c27b0 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-warning{background-size:200% 200%;background-position:50%}.btn-warning:focus{background-color:#ff9800}.btn-warning:hover,.btn-warning:active:hover{background-color:#e08600}.btn-warning:active{background-color:#c27400;background-image:radial-gradient(circle, #c27400 10%, #ff9800 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-danger{background-size:200% 200%;background-position:50%}.btn-danger:focus{background-color:#e51c23}.btn-danger:hover,.btn-danger:active:hover{background-color:#cb171e}.btn-danger:active{background-color:#b0141a;background-image:radial-gradient(circle, #b0141a 10%, #e51c23 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn-link{background-size:200% 200%;background-position:50%}.btn-link:focus{background-color:#ffffff}.btn-link:hover,.btn-link:active:hover{background-color:#f0f0f0}.btn-link:active{background-color:#e0e0e0;background-image:radial-gradient(circle, #e0e0e0 10%, #fff 11%);background-repeat:no-repeat;background-size:1000% 1000%;box-shadow:2px 2px 4px rgba(0,0,0,0.4)}.btn{text-transform:uppercase;border:none;box-shadow:1px 1px 4px rgba(0,0,0,0.4);transition:all 0.4s}.btn-link{border-radius:3px;box-shadow:none;color:#444444}.btn-link:hover,.btn-link:focus{box-shadow:none;color:#444444;text-decoration:none}.btn-default.disabled{background-color:rgba(0,0,0,0.1);color:rgba(0,0,0,0.4);opacity:1}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:0}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:0}body{-webkit-font-smoothing:antialiased;letter-spacing:.1px}p{margin:0 0 1em}input,button{-webkit-font-smoothing:antialiased;letter-spacing:.1px}a{transition:all 0.2s}.table-hover>tbody>tr,.table-hover>tbody>tr>th,.table-hover>tbody>tr>td{transition:all 0.2s}label{font-weight:normal}textarea,textarea.form-control,input.form-control,input[type=text],input[type=password],input[type=email],input[type=number],[type=text].form-control,[type=password].form-control,[type=email].form-control,[type=tel].form-control,[contenteditable].form-control{padding:0;border:none;border-radius:0;-webkit-appearance:none;box-shadow:inset 0 -1px 0 #ddd;font-size:16px}textarea:focus,textarea.form-control:focus,input.form-control:focus,input[type=text]:focus,input[type=password]:focus,input[type=email]:focus,input[type=number]:focus,[type=text].form-control:focus,[type=password].form-control:focus,[type=email].form-control:focus,[type=tel].form-control:focus,[contenteditable].form-control:focus{box-shadow:inset 0 -2px 0 #2196f3}textarea[disabled],textarea.form-control[disabled],input.form-control[disabled],input[type=text][disabled],input[type=password][disabled],input[type=email][disabled],input[type=number][disabled],[type=text].form-control[disabled],[type=password].form-control[disabled],[type=email].form-control[disabled],[type=tel].form-control[disabled],[contenteditable].form-control[disabled],textarea[readonly],textarea.form-control[readonly],input.form-control[readonly],input[type=text][readonly],input[type=password][readonly],input[type=email][readonly],input[type=number][readonly],[type=text].form-control[readonly],[type=password].form-control[readonly],[type=email].form-control[readonly],[type=tel].form-control[readonly],[contenteditable].form-control[readonly]{box-shadow:none;border-bottom:1px dotted #ddd}textarea.input-sm,textarea.form-control.input-sm,input.form-control.input-sm,input[type=text].input-sm,input[type=password].input-sm,input[type=email].input-sm,input[type=number].input-sm,[type=text].form-control.input-sm,[type=password].form-control.input-sm,[type=email].form-control.input-sm,[type=tel].form-control.input-sm,[contenteditable].form-control.input-sm{font-size:12px}textarea.input-lg,textarea.form-control.input-lg,input.form-control.input-lg,input[type=text].input-lg,input[type=password].input-lg,input[type=email].input-lg,input[type=number].input-lg,[type=text].form-control.input-lg,[type=password].form-control.input-lg,[type=email].form-control.input-lg,[type=tel].form-control.input-lg,[contenteditable].form-control.input-lg{font-size:17px}select,select.form-control{border:0;border-radius:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;padding-left:0;padding-right:0\9;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEVmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZmaP/QSjAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=);background-size:13px;background-repeat:no-repeat;background-position:right center;box-shadow:inset 0 -1px 0 #ddd;font-size:16px;line-height:1.5}select::-ms-expand,select.form-control::-ms-expand{display:none}select.input-sm,select.form-control.input-sm{font-size:12px}select.input-lg,select.form-control.input-lg{font-size:17px}select:focus,select.form-control:focus{box-shadow:inset 0 -2px 0 #2196f3;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAAJ1BMVEUhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISEhISF8S9ewAAAADHRSTlMAAgMJC0uWpKa6wMxMdjkoAAAANUlEQVR4AeXJyQEAERAAsNl7Hf3X6xt0QL6JpZWq30pdvdadme+0PMdzvHm8YThHcT1H7K0BtOMDniZhWOgAAAAASUVORK5CYII=)}select[multiple],select.form-control[multiple]{background:none}.radio label,.radio-inline label,.checkbox label,.checkbox-inline label{padding-left:25px}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="radio"],.checkbox-inline input[type="radio"],.radio input[type="checkbox"],.radio-inline input[type="checkbox"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{margin-left:-25px}input[type="radio"],.radio input[type="radio"],.radio-inline input[type="radio"]{position:relative;margin-top:6px;margin-right:4px;vertical-align:top;border:none;background-color:transparent;-webkit-appearance:none;appearance:none;cursor:pointer}input[type="radio"]:focus,.radio input[type="radio"]:focus,.radio-inline input[type="radio"]:focus{outline:none}input[type="radio"]:before,.radio input[type="radio"]:before,.radio-inline input[type="radio"]:before,input[type="radio"]:after,.radio input[type="radio"]:after,.radio-inline input[type="radio"]:after{content:"";display:block;width:18px;height:18px;border-radius:50%;transition:240ms}input[type="radio"]:before,.radio input[type="radio"]:before,.radio-inline input[type="radio"]:before{position:absolute;left:0;top:-3px;background-color:#2196f3;-webkit-transform:scale(0);transform:scale(0)}input[type="radio"]:after,.radio input[type="radio"]:after,.radio-inline input[type="radio"]:after{position:relative;top:-3px;border:2px solid #666666}input[type="radio"]:checked:before,.radio input[type="radio"]:checked:before,.radio-inline input[type="radio"]:checked:before{-webkit-transform:scale(.5);transform:scale(.5)}input[type="radio"]:disabled:checked:before,.radio input[type="radio"]:disabled:checked:before,.radio-inline input[type="radio"]:disabled:checked:before{background-color:#bbbbbb}input[type="radio"]:checked:after,.radio input[type="radio"]:checked:after,.radio-inline input[type="radio"]:checked:after{border-color:#2196f3}input[type="radio"]:disabled:after,.radio input[type="radio"]:disabled:after,.radio-inline input[type="radio"]:disabled:after,input[type="radio"]:disabled:checked:after,.radio input[type="radio"]:disabled:checked:after,.radio-inline input[type="radio"]:disabled:checked:after{border-color:#bbbbbb}input[type="checkbox"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:relative;border:none;margin-bottom:-4px;-webkit-appearance:none;appearance:none;cursor:pointer}input[type="checkbox"]:focus,.checkbox input[type="checkbox"]:focus,.checkbox-inline input[type="checkbox"]:focus{outline:none}input[type="checkbox"]:focus:after,.checkbox input[type="checkbox"]:focus:after,.checkbox-inline input[type="checkbox"]:focus:after{border-color:#2196f3}input[type="checkbox"]:after,.checkbox input[type="checkbox"]:after,.checkbox-inline input[type="checkbox"]:after{content:"";display:block;width:18px;height:18px;margin-top:-2px;margin-right:5px;border:2px solid #666666;border-radius:2px;transition:240ms}input[type="checkbox"]:checked:before,.checkbox input[type="checkbox"]:checked:before,.checkbox-inline input[type="checkbox"]:checked:before{content:"";position:absolute;top:0;left:6px;display:table;width:6px;height:12px;border:2px solid #fff;border-top-width:0;border-left-width:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}input[type="checkbox"]:checked:after,.checkbox input[type="checkbox"]:checked:after,.checkbox-inline input[type="checkbox"]:checked:after{background-color:#2196f3;border-color:#2196f3}input[type="checkbox"]:disabled:after,.checkbox input[type="checkbox"]:disabled:after,.checkbox-inline input[type="checkbox"]:disabled:after{border-color:#bbbbbb}input[type="checkbox"]:disabled:checked:after,.checkbox input[type="checkbox"]:disabled:checked:after,.checkbox-inline input[type="checkbox"]:disabled:checked:after{background-color:#bbbbbb;border-color:transparent}.has-warning input:not([type=checkbox]),.has-warning .form-control,.has-warning input.form-control[readonly],.has-warning input[type=text][readonly],.has-warning [type=text].form-control[readonly],.has-warning input:not([type=checkbox]):focus,.has-warning .form-control:focus{border-bottom:none;box-shadow:inset 0 -2px 0 #ff9800}.has-error input:not([type=checkbox]),.has-error .form-control,.has-error input.form-control[readonly],.has-error input[type=text][readonly],.has-error [type=text].form-control[readonly],.has-error input:not([type=checkbox]):focus,.has-error .form-control:focus{border-bottom:none;box-shadow:inset 0 -2px 0 #e51c23}.has-success input:not([type=checkbox]),.has-success .form-control,.has-success input.form-control[readonly],.has-success input[type=text][readonly],.has-success [type=text].form-control[readonly],.has-success input:not([type=checkbox]):focus,.has-success .form-control:focus{border-bottom:none;box-shadow:inset 0 -2px 0 #4caf50}.has-warning .input-group-addon,.has-error .input-group-addon,.has-success .input-group-addon{color:#666666;border-color:transparent;background-color:transparent}.form-group-lg select,.form-group-lg select.form-control{line-height:1.5}.nav-tabs>li>a,.nav-tabs>li>a:focus{margin-right:0;background-color:transparent;border:none;color:#666666;box-shadow:inset 0 -1px 0 #ddd;transition:all 0.2s}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus:hover{background-color:transparent;box-shadow:inset 0 -2px 0 #2196f3;color:#2196f3}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus{border:none;box-shadow:inset 0 -2px 0 #2196f3;color:#2196f3}.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus:hover{border:none;color:#2196f3}.nav-tabs>li.disabled>a{box-shadow:inset 0 -1px 0 #ddd}.nav-tabs.nav-justified>li>a,.nav-tabs.nav-justified>li>a:hover,.nav-tabs.nav-justified>li>a:focus,.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:none}.nav-tabs .dropdown-menu{margin-top:0}.dropdown-menu{margin-top:0;border:none;box-shadow:0 1px 4px rgba(0,0,0,0.3)}.alert{border:none;color:#fff}.alert-success{background-color:#4caf50}.alert-info{background-color:#9c27b0}.alert-warning{background-color:#ff9800}.alert-danger{background-color:#e51c23}.alert a:not(.close):not(.btn),.alert .alert-link{color:#fff;font-weight:bold}.alert .close{color:#fff}.badge{padding:4px 6px 4px}.progress{position:relative;z-index:1;height:6px;border-radius:0;box-shadow:none}.progress-bar{box-shadow:none}.progress-bar:last-child{border-radius:0 3px 3px 0}.progress-bar:last-child:before{display:block;content:"";position:absolute;width:100%;height:100%;left:0;right:0;z-index:-1;background-color:#cae6fc}.progress-bar-success:last-child.progress-bar:before{background-color:#c7e7c8}.progress-bar-info:last-child.progress-bar:before{background-color:#edc9f3}.progress-bar-warning:last-child.progress-bar:before{background-color:#ffe0b3}.progress-bar-danger:last-child.progress-bar:before{background-color:#f28e92}.close{font-size:34px;font-weight:300;line-height:24px;opacity:0.6;transition:all 0.2s}.close:hover{opacity:1}.list-group-item{padding:15px}.list-group-item-text{color:#bbbbbb}.well{border-radius:0;box-shadow:none}.panel{border:none;border-radius:2px;box-shadow:0 1px 4px rgba(0,0,0,0.3)}.panel-heading{border-bottom:none}.panel-footer{border-top:none}.popover{border:none;box-shadow:0 1px 4px rgba(0,0,0,0.3)}.carousel-caption h1,.carousel-caption h2,.carousel-caption h3,.carousel-caption h4,.carousel-caption h5,.carousel-caption h6{color:inherit} diff --git a/themes/readable.css b/themes/readable.css new file mode 100644 index 00000000..1215382a --- /dev/null +++ b/themes/readable.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Raleway:400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:Georgia,"Times New Roman",Times,serif;font-size:16px;line-height:1.42857143;color:#333333;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#4582ec;text-decoration:none}a:hover,a:focus{color:#134fb8;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:22px;margin-bottom:22px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:bold;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#b3b3b3}h1,.h1,h2,.h2,h3,.h3{margin-top:22px;margin-bottom:11px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:11px;margin-bottom:11px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:41px}h2,.h2{font-size:34px}h3,.h3{font-size:28px}h4,.h4{font-size:20px}h5,.h5{font-size:16px}h6,.h6{font-size:14px}p{margin:0 0 11px}.lead{margin-bottom:22px;font-size:18px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:24px}}small,.small{font-size:87%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#b3b3b3}.text-primary{color:#4582ec}a.text-primary:hover,a.text-primary:focus{color:#1863e6}.text-success{color:#3fad46}a.text-success:hover,a.text-success:focus{color:#318837}.text-info{color:#5bc0de}a.text-info:hover,a.text-info:focus{color:#31b0d5}.text-warning{color:#f0ad4e}a.text-warning:hover,a.text-warning:focus{color:#ec971f}.text-danger{color:#d9534f}a.text-danger:hover,a.text-danger:focus{color:#c9302c}.bg-primary{color:#fff;background-color:#4582ec}a.bg-primary:hover,a.bg-primary:focus{background-color:#1863e6}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:10px;margin:44px 0 22px;border-bottom:1px solid #dddddd}ul,ol{margin-top:0;margin-bottom:11px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:22px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:11px 22px;margin:0 0 22px;font-size:20px;border-left:5px solid #4582ec}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#333333}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #4582ec;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:22px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10.5px;margin:0 0 11px;font-size:15px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#b3b3b3;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:22px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:16.5px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:22px;font-size:24px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:16px;line-height:1.42857143;color:#333333}.form-control{display:block;width:100%;height:40px;padding:8px 12px;font-size:16px;line-height:1.42857143;color:#333333;background-color:#ffffff;background-image:none;border:1px solid #dddddd;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#b3b3b3;opacity:1}.form-control:-ms-input-placeholder{color:#b3b3b3}.form-control::-webkit-input-placeholder{color:#b3b3b3}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:40px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:33px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:57px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:22px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:38px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:33px;padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}select.input-sm{height:33px;line-height:33px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:33px;padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:33px;line-height:33px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:33px;min-height:36px;padding:6px 10px;font-size:14px;line-height:1.5}.input-lg{height:57px;padding:14px 16px;font-size:20px;line-height:1.3333333;border-radius:6px}select.input-lg{height:57px;line-height:57px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:57px;padding:14px 16px;font-size:20px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:57px;line-height:57px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:57px;min-height:42px;padding:15px 16px;font-size:20px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:50px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:40px;height:40px;line-height:40px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:57px;height:57px;line-height:57px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:33px;height:33px;line-height:33px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3fad46}.has-success .form-control{border-color:#3fad46;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#318837;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #81d186}.has-success .input-group-addon{color:#3fad46;background-color:#dff0d8;border-color:#3fad46}.has-success .form-control-feedback{color:#3fad46}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#f0ad4e}.has-warning .form-control{border-color:#f0ad4e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#ec971f;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f8d9ac}.has-warning .input-group-addon{color:#f0ad4e;background-color:#fcf8e3;border-color:#f0ad4e}.has-warning .form-control-feedback{color:#f0ad4e}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#d9534f}.has-error .form-control{border-color:#d9534f;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#c9302c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3}.has-error .input-group-addon{color:#d9534f;background-color:#f2dede;border-color:#d9534f}.has-error .form-control-feedback{color:#d9534f}.has-feedback label~.form-control-feedback{top:27px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:31px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:20px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:14px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:16px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333333;background-color:#ffffff;border-color:#dddddd}.btn-default:focus,.btn-default.focus{color:#333333;background-color:#e6e6e6;border-color:#9d9d9d}.btn-default:hover{color:#333333;background-color:#e6e6e6;border-color:#bebebe}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333333;background-color:#e6e6e6;background-image:none;border-color:#bebebe}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333333;background-color:#d4d4d4;border-color:#9d9d9d}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#ffffff;border-color:#dddddd}.btn-default .badge{color:#ffffff;background-color:#333333}.btn-primary{color:#ffffff;background-color:#4582ec;border-color:#4582ec}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#1863e6;border-color:#1045a1}.btn-primary:hover{color:#ffffff;background-color:#1863e6;border-color:#175fdd}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#1863e6;background-image:none;border-color:#175fdd}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1455c6;border-color:#1045a1}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#4582ec;border-color:#4582ec}.btn-primary .badge{color:#4582ec;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#3fad46;border-color:#3fad46}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#318837;border-color:#1d5020}.btn-success:hover{color:#ffffff;background-color:#318837;border-color:#2f8034}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#318837;background-image:none;border-color:#2f8034}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#286d2c;border-color:#1d5020}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#3fad46;border-color:#3fad46}.btn-success .badge{color:#3fad46;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:#1f7e9a}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#31b0d5;background-image:none;border-color:#2aabd2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#269abc;border-color:#1f7e9a}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info .badge{color:#5bc0de;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#ec971f;border-color:#b06d0f}.btn-warning:hover{color:#ffffff;background-color:#ec971f;border-color:#eb9316}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#ec971f;background-image:none;border-color:#eb9316}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#d58512;border-color:#b06d0f}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:#f0ad4e}.btn-warning .badge{color:#f0ad4e;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#d9534f;border-color:#d9534f}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#c9302c;border-color:#8b211e}.btn-danger:hover{color:#ffffff;background-color:#c9302c;border-color:#c12e2a}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#c9302c;background-image:none;border-color:#c12e2a}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#ac2925;border-color:#8b211e}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:#d9534f}.btn-danger .badge{color:#d9534f;background-color:#ffffff}.btn-link{font-weight:400;color:#4582ec;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#134fb8;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#b3b3b3;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:20px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:14px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:16px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:10px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#4582ec}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#4582ec;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#b3b3b3}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:14px;line-height:1.42857143;color:#b3b3b3;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:57px;padding:14px 16px;font-size:20px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:57px;line-height:57px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:33px;padding:5px 10px;font-size:14px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:33px;line-height:33px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:16px;font-weight:400;line-height:1;color:#333333;text-align:center;background-color:#eeeeee;border:1px solid #dddddd;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:14px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:20px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#b3b3b3}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#b3b3b3;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#4582ec}.nav .nav-divider{height:1px;margin:10px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555555;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#4582ec}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:65px;margin-bottom:22px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:65px;padding:21.5px 15px;font-size:20px;line-height:22px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:15.5px;margin-bottom:15.5px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:10.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:22px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:22px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:21.5px;padding-bottom:21.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:12.5px;margin-bottom:12.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:12.5px;margin-bottom:12.5px}.navbar-btn.btn-sm{margin-top:16px;margin-bottom:16px}.navbar-btn.btn-xs{margin-top:21.5px;margin-bottom:21.5px}.navbar-text{margin-top:21.5px;margin-bottom:21.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#ffffff;border-color:#dddddd}.navbar-default .navbar-brand{color:#4582ec}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#4582ec;background-color:transparent}.navbar-default .navbar-text{color:#333333}.navbar-default .navbar-nav>li>a{color:#4582ec}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#4582ec;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#4582ec;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#333333;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#4582ec;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#4582ec}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#4582ec;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#4582ec;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#333333;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#dddddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#dddddd}.navbar-default .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#dddddd}.navbar-default .navbar-link{color:#4582ec}.navbar-default .navbar-link:hover{color:#4582ec}.navbar-default .btn-link{color:#4582ec}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#4582ec}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#333333}.navbar-inverse{background-color:#ffffff;border-color:#dddddd}.navbar-inverse .navbar-brand{color:#333333}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-text{color:#333333}.navbar-inverse .navbar-nav>li>a{color:#333333}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#333333;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#dddddd}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#dddddd}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#333333}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#333333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#dddddd}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#dddddd}.navbar-inverse .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#ededed}.navbar-inverse .navbar-link{color:#333333}.navbar-inverse .navbar-link:hover{color:#333333}.navbar-inverse .btn-link{color:#333333}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#333333}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:22px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#b3b3b3}.pagination{display:inline-block;padding-left:0;margin:22px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#333333;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#4582ec;border-color:#4582ec}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#4582ec;border-color:#4582ec}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#b3b3b3;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:20px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:14px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:22px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#4582ec}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#b3b3b3;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#ffffff}.label-default[href]:hover,.label-default[href]:focus{background-color:#e6e6e6}.label-primary{background-color:#4582ec}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#1863e6}.label-success{background-color:#3fad46}.label-success[href]:hover,.label-success[href]:focus{background-color:#318837}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:14px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#4582ec;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#4582ec;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#f7f7f7}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:24px;font-weight:200}.jumbotron>hr{border-top-color:#dedede}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:72px}}.thumbnail{display:block;padding:4px;margin-bottom:22px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#4582ec}.thumbnail .caption{padding:9px;color:#333333}.alert{padding:15px;margin-bottom:22px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#3fad46;border-color:#3fad46}.alert-success hr{border-top-color:#389a3e}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.alert-info hr{border-top-color:#46b8da}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#f0ad4e;border-color:#f0ad4e}.alert-warning hr{border-top-color:#eea236}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#d9534f;border-color:#d9534f}.alert-danger hr{border-top-color:#d43f3a}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:22px;margin-bottom:22px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:14px;line-height:22px;color:#ffffff;text-align:center;background-color:#4582ec;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#3fad46}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#b3b3b3;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#b3b3b3}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#4582ec;border-color:#4582ec}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#fefeff}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3fad46;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3fad46}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#3fad46;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#3fad46;border-color:#3fad46}.list-group-item-info{color:#5bc0de;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#5bc0de}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#5bc0de;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.list-group-item-warning{color:#f0ad4e;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#f0ad4e}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#f0ad4e;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#f0ad4e;border-color:#f0ad4e}.list-group-item-danger{color:#d9534f;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#d9534f}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#d9534f;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#d9534f;border-color:#d9534f}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:22px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:18px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#ffffff;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:22px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#4582ec}.panel-primary>.panel-heading{color:#ffffff;background-color:#4582ec;border-color:#4582ec}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#4582ec}.panel-primary>.panel-heading .badge{color:#4582ec;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#4582ec}.panel-success{border-color:#3fad46}.panel-success>.panel-heading{color:#ffffff;background-color:#3fad46;border-color:#3fad46}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3fad46}.panel-success>.panel-heading .badge{color:#3fad46;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3fad46}.panel-info{border-color:#5bc0de}.panel-info>.panel-heading{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#5bc0de}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#5bc0de}.panel-warning{border-color:#f0ad4e}.panel-warning>.panel-heading{color:#ffffff;background-color:#f0ad4e;border-color:#f0ad4e}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f0ad4e}.panel-warning>.panel-heading .badge{color:#f0ad4e;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f0ad4e}.panel-danger{border-color:#d9534f}.panel-danger>.panel-heading{color:#ffffff;background-color:#d9534f;border-color:#d9534f}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d9534f}.panel-danger>.panel-heading .badge{color:#d9534f;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d9534f}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f7f7f7;border:1px solid #e5e5e5;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:24px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:Georgia,"Times New Roman",Times,serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:Georgia,"Times New Roman",Times,serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:16px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:16px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}.navbar-nav,.navbar-form{margin-left:0;margin-right:0}.navbar-nav>li>a{margin:12.5px 6px;padding:8px 12px;border:1px solid transparent;border-radius:4px}.navbar-nav>li>a:hover{border:1px solid #ddd}.navbar-nav>.active>a,.navbar-nav>.active>a:hover{border:1px solid #ddd}.navbar-default .navbar-nav>.active>a:hover{color:#4582ec}.navbar-inverse .navbar-nav>.active>a:hover{color:#333333}.navbar-brand{padding-top:12.5px;padding-bottom:12.5px;line-height:1.9}@media (min-width:768px){.navbar .navbar-nav>li>a{padding:8px 12px}}@media (max-width:767px){.navbar .navbar-nav>li>a{margin:0}}.btn{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}legend{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}.input-group-addon{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border:1px solid #ddd}.pagination{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 24px}.pager{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif}.pager a{color:#333333}.pager a:hover{border-color:transparent;color:#fff}.pager .disabled a{border-color:#dddddd}.close{color:#fff;text-decoration:none;text-shadow:none;opacity:0.4}.close:hover,.close:focus{color:#fff;opacity:1}.alert .alert-link{color:#ffffff;text-decoration:underline}.label{font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:normal}.label-default{border:1px solid #ddd;color:#333333}.badge{padding:1px 7px 5px;vertical-align:2px;font-family:"Raleway","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:normal}.panel{box-shadow:none}.panel-default .close{color:#333333}.modal .close{color:#333333} diff --git a/themes/sandstone.css b/themes/sandstone.css new file mode 100644 index 00000000..5cbb40ba --- /dev/null +++ b/themes/sandstone.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#3e3f3a;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#93c54b;text-decoration:none}a:hover,a:focus{color:#79a736;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#f8f5f0;border:1px solid #dfd7ca;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #f8f5f0}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#98978b}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#98978b}.text-primary{color:#325d88}a.text-primary:hover,a.text-primary:focus{color:#244363}.text-success{color:#93c54b}a.text-success:hover,a.text-success:focus{color:#79a736}.text-info{color:#29abe0}a.text-info:hover,a.text-info:focus{color:#1b8dbb}.text-warning{color:#f47c3c}a.text-warning:hover,a.text-warning:focus{color:#ef5c0e}.text-danger{color:#d9534f}a.text-danger:hover,a.text-danger:focus{color:#c9302c}.bg-primary{color:#fff;background-color:#325d88}a.bg-primary:hover,a.bg-primary:focus{background-color:#244363}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #f8f5f0}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #dfd7ca}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#3e3f3a}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #dfd7ca;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#8e8c84;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#98978b;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dfd7ca}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dfd7ca}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dfd7ca}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dfd7ca}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dfd7ca}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f8f5f0}.table-hover>tbody>tr:hover{background-color:#f8f5f0}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f8f5f0}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#f0e9df}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dfd7ca}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:inherit;border:0;border-bottom:1px solid transparent}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:13px;font-size:14px;line-height:1.42857143;color:#3e3f3a}.form-control{display:block;width:100%;height:46px;padding:12px 16px;font-size:14px;line-height:1.42857143;color:#3e3f3a;background-color:#ffffff;background-image:none;border:1px solid #dfd7ca;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:transparent;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6)}.form-control::-moz-placeholder{color:#dfd7ca;opacity:1}.form-control:-ms-input-placeholder{color:#dfd7ca}.form-control::-webkit-input-placeholder{color:#dfd7ca}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#f8f5f0;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:46px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:66px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:13px;padding-bottom:13px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:66px;padding:20px 30px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:66px;line-height:66px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:66px;padding:20px 30px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:66px;line-height:66px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:66px;min-height:38px;padding:21px 30px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:57.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:46px;height:46px;line-height:46px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:66px;height:66px;line-height:66px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#93c54b}.has-success .form-control{border-color:#93c54b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#79a736;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c1de98}.has-success .input-group-addon{color:#93c54b;background-color:#dff0d8;border-color:#93c54b}.has-success .form-control-feedback{color:#93c54b}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#f47c3c}.has-warning .form-control{border-color:#f47c3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#ef5c0e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f9bd9d}.has-warning .input-group-addon{color:#f47c3c;background-color:#fcf8e3;border-color:#f47c3c}.has-warning .form-control-feedback{color:#f47c3c}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#d9534f}.has-error .form-control{border-color:#d9534f;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#c9302c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #eba5a3}.has-error .input-group-addon{color:#d9534f;background-color:#f2dede;border-color:#d9534f}.has-error .form-control-feedback{color:#d9534f}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#7f8177}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:13px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:33px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:13px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:21px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:12px 16px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#3e3f3a;border-color:transparent}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#242422;border-color:rgba(0,0,0,0)}.btn-default:hover{color:#ffffff;background-color:#242422;border-color:rgba(0,0,0,0)}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#242422;background-image:none;border-color:rgba(0,0,0,0)}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#121210;border-color:rgba(0,0,0,0)}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#3e3f3a;border-color:transparent}.btn-default .badge{color:#3e3f3a;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#325d88;border-color:transparent}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#244363;border-color:rgba(0,0,0,0)}.btn-primary:hover{color:#ffffff;background-color:#244363;border-color:rgba(0,0,0,0)}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#244363;background-image:none;border-color:rgba(0,0,0,0)}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#1b3249;border-color:rgba(0,0,0,0)}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#325d88;border-color:transparent}.btn-primary .badge{color:#325d88;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#93c54b;border-color:transparent}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#79a736;border-color:rgba(0,0,0,0)}.btn-success:hover{color:#ffffff;background-color:#79a736;border-color:rgba(0,0,0,0)}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#79a736;background-image:none;border-color:rgba(0,0,0,0)}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#658c2d;border-color:rgba(0,0,0,0)}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#93c54b;border-color:transparent}.btn-success .badge{color:#93c54b;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#29abe0;border-color:transparent}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#1b8dbb;border-color:rgba(0,0,0,0)}.btn-info:hover{color:#ffffff;background-color:#1b8dbb;border-color:rgba(0,0,0,0)}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#1b8dbb;background-image:none;border-color:rgba(0,0,0,0)}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#17759c;border-color:rgba(0,0,0,0)}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#29abe0;border-color:transparent}.btn-info .badge{color:#29abe0;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f47c3c;border-color:transparent}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#ef5c0e;border-color:rgba(0,0,0,0)}.btn-warning:hover{color:#ffffff;background-color:#ef5c0e;border-color:rgba(0,0,0,0)}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#ef5c0e;background-image:none;border-color:rgba(0,0,0,0)}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#ce4f0c;border-color:rgba(0,0,0,0)}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f47c3c;border-color:transparent}.btn-warning .badge{color:#f47c3c;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#d9534f;border-color:transparent}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#c9302c;border-color:rgba(0,0,0,0)}.btn-danger:hover{color:#ffffff;background-color:#c9302c;border-color:rgba(0,0,0,0)}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#c9302c;background-image:none;border-color:rgba(0,0,0,0)}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#ac2925;border-color:rgba(0,0,0,0)}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:transparent}.btn-danger .badge{color:#d9534f;background-color:#ffffff}.btn-link{font-weight:400;color:#93c54b;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#79a736;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#dfd7ca;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:20px 30px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #dfd7ca;border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#f8f5f0}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#98978b;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#98978b;text-decoration:none;background-color:#f8f5f0}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#98978b;text-decoration:none;background-color:#f8f5f0;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#dfd7ca}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#dfd7ca;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:66px;padding:20px 30px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:66px;line-height:66px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:12px 16px;font-size:14px;font-weight:400;line-height:1;color:#3e3f3a;text-align:center;background-color:#f8f5f0;border:1px solid #dfd7ca;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:20px 30px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#f8f5f0}.nav>li.disabled>a{color:#dfd7ca}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#dfd7ca;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#f8f5f0;border-color:#93c54b}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dfd7ca}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#dfd7ca #dfd7ca #dfd7ca}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#98978b;cursor:default;background-color:#ffffff;border:1px solid #dfd7ca;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dfd7ca}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dfd7ca;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#98978b;background-color:#f8f5f0}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dfd7ca}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dfd7ca;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:60px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:60px;padding:20px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:13px;margin-bottom:13px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:10px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:20px;padding-bottom:20px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:7px;margin-bottom:7px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:7px;margin-bottom:7px}.navbar-btn.btn-sm{margin-top:15px;margin-bottom:15px}.navbar-btn.btn-xs{margin-top:19px;margin-bottom:19px}.navbar-text{margin-top:20px;margin-bottom:20px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#3e3f3a;border-color:#3e3f3a}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-text{color:#8e8c84}.navbar-default .navbar-nav>li>a{color:#98978b}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#393a35}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#393a35}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#98978b}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#393a35}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#393a35}.navbar-default .navbar-toggle .icon-bar{background-color:#98978b}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#3e3f3a}.navbar-default .navbar-link{color:#98978b}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#98978b}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#93c54b;border-color:#93c54b}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#dfd7ca}.navbar-inverse .navbar-nav>li>a{color:#6b9430}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#89be3d}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#89be3d}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#93c54b}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#93c54b}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#6b9430}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#89be3d}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#89be3d}.navbar-inverse .navbar-toggle .icon-bar{background-color:#6b9430}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#81b33a}.navbar-inverse .navbar-link{color:#6b9430}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#6b9430}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f8f5f0;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#dfd7ca;content:"/\00a0"}.breadcrumb>.active{color:#98978b}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:12px 16px;margin-left:-1px;line-height:1.42857143;color:#98978b;text-decoration:none;background-color:#f8f5f0;border:1px solid #dfd7ca}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#8e8c84;background-color:#dfd7ca;border-color:#dfd7ca}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#8e8c84;cursor:default;background-color:#dfd7ca;border-color:#dfd7ca}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#dfd7ca;cursor:not-allowed;background-color:#f8f5f0;border-color:#dfd7ca}.pagination-lg>li>a,.pagination-lg>li>span{padding:20px 30px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#f8f5f0;border:1px solid #dfd7ca;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#dfd7ca}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#dfd7ca;cursor:not-allowed;background-color:#f8f5f0}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#3e3f3a}.label-default[href]:hover,.label-default[href]:focus{background-color:#242422}.label-primary{background-color:#325d88}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#244363}.label-success{background-color:#93c54b}.label-success[href]:hover,.label-success[href]:focus{background-color:#79a736}.label-info{background-color:#29abe0}.label-info[href]:hover,.label-info[href]:focus{background-color:#1b8dbb}.label-warning{background-color:#f47c3c}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ef5c0e}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:normal;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#93c54b;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#ffffff;background-color:#93c54b}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#f8f5f0}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#e8decd}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#f8f5f0;border:1px solid #dfd7ca;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#93c54b}.thumbnail .caption{padding:9px;color:#3e3f3a}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#93c54b;border-color:transparent}.alert-success hr{border-top-color:rgba(0,0,0,0)}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#29abe0;border-color:transparent}.alert-info hr{border-top-color:rgba(0,0,0,0)}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#f47c3c;border-color:transparent}.alert-warning hr{border-top-color:rgba(0,0,0,0)}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#d9534f;border-color:transparent}.alert-danger hr{border-top-color:rgba(0,0,0,0)}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#325d88;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#93c54b}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#29abe0}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f47c3c}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dfd7ca}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#dfd7ca;cursor:not-allowed;background-color:#f8f5f0}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#dfd7ca}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#3e3f3a;background-color:#f8f5f0;border-color:#dfd7ca}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#3e3f3a}a.list-group-item,button.list-group-item{color:#3e3f3a}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:inherit}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#3e3f3a;text-decoration:none;background-color:#f8f5f0}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#93c54b;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#93c54b}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#93c54b;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#93c54b;border-color:#93c54b}.list-group-item-info{color:#29abe0;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#29abe0}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#29abe0;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#29abe0;border-color:#29abe0}.list-group-item-warning{color:#f47c3c;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#f47c3c}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#f47c3c;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#f47c3c;border-color:#f47c3c}.list-group-item-danger{color:#d9534f;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#d9534f}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#d9534f;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#d9534f;border-color:#d9534f}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f8f5f0;border-top:1px solid #dfd7ca;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dfd7ca}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dfd7ca}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dfd7ca}.panel-default{border-color:#dfd7ca}.panel-default>.panel-heading{color:#3e3f3a;background-color:#f8f5f0;border-color:#dfd7ca}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dfd7ca}.panel-default>.panel-heading .badge{color:#f8f5f0;background-color:#3e3f3a}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dfd7ca}.panel-primary{border-color:#325d88}.panel-primary>.panel-heading{color:#ffffff;background-color:#325d88;border-color:#325d88}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#325d88}.panel-primary>.panel-heading .badge{color:#325d88;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#325d88}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#93c54b;background-color:#93c54b;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#93c54b;background-color:#93c54b}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#29abe0;background-color:#29abe0;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#29abe0;background-color:#29abe0}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#f47c3c;background-color:#f47c3c;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#f47c3c;background-color:#f47c3c}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#d9534f;background-color:#d9534f;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#d9534f;background-color:#d9534f}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f8f5f0;border:1px solid transparent;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 0 0 transparent;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #f8f5f0;border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #f8f5f0}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #f8f5f0}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=100);opacity:1}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#3e3f3a}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#3e3f3a}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#3e3f3a}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#3e3f3a}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#3e3f3a}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#3e3f3a}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#3e3f3a}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#3e3f3a}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#3e3f3a;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Roboto","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #dfd7ca;border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#b9a78a;border-top-color:#dfd7ca;border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#b9a78a;border-right-color:#dfd7ca;border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#b9a78a;border-bottom-color:#dfd7ca}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#b9a78a;border-left-color:#dfd7ca}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f8f5f0;border-bottom:1px solid #f0e9df;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.sandstone{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.navbar .nav>li>a{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.navbar-form input,.navbar-form .form-control{border:none}.btn{border:none;font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.btn:hover{border-color:transparent}.btn-lg{line-height:26px}.btn-default:hover{background-color:#393a35}input,.form-control{box-shadow:none}input:focus,.form-control:focus{border-color:#dfd7ca;box-shadow:none}.nav{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:#dfd7ca}.nav-tabs>li>a{background-color:#f8f5f0;border-color:#dfd7ca;color:#98978b}.nav-tabs>li.disabled>a:hover{background-color:#f8f5f0}.nav-pills a{color:#98978b}.nav-pills li>a{border:1px solid transparent}.nav-pills li.active>a,.nav-pills li>a:hover{border-color:#dfd7ca}.nav-pills li.disabled>a{border-color:transparent}.breadcrumb{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase;border:1px solid #dfd7ca}.breadcrumb a{color:#98978b}.pagination{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.pager{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.pager li>a{color:#98978b}.dropdown-menu>li>a{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.alert a,.alert .alert-link{color:#fff}.tooltip{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.progress{border-radius:10px;background-color:#dfd7ca;box-shadow:none}.progress-bar{box-shadow:none}.list-group-item{padding:16px 24px}.well{box-shadow:none}.panel{box-shadow:none}.panel .panel-heading,.panel .panel-title{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase;color:#fff}.panel .panel-footer{font-size:11px;line-height:22px;font-weight:500;text-transform:uppercase}.panel-default .panel-heading,.panel-default .panel-title,.panel-default .panel-footer{color:#98978b} diff --git a/themes/simplex.css b/themes/simplex.css new file mode 100644 index 00000000..641237b5 --- /dev/null +++ b/themes/simplex.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;line-height:1.42857143;color:#777777;background-color:#fcfcfc}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#d9230f;text-decoration:none}a:hover,a:focus{color:#91170a;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fcfcfc;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:18px;margin-bottom:18px;border:0;border-top:1px solid #dddddd}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;line-height:1.1;color:#444444}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#808080}h1,.h1,h2,.h2,h3,.h3{margin-top:18px;margin-bottom:9px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:9px;margin-bottom:9px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:33px}h2,.h2{font-size:27px}h3,.h3{font-size:23px}h4,.h4{font-size:17px}h5,.h5{font-size:13px}h6,.h6{font-size:12px}p{margin:0 0 9px}.lead{margin-bottom:18px;font-size:14px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:19.5px}}small,.small{font-size:92%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#808080}.text-primary{color:#d9230f}a.text-primary:hover,a.text-primary:focus{color:#a91b0c}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#d9230f}a.bg-primary:hover,a.bg-primary:focus{background-color:#a91b0c}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:8px;margin:36px 0 18px;border-bottom:1px solid #dddddd}ul,ol{margin-top:0;margin-bottom:9px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:18px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:9px 18px;margin:0 0 18px;font-size:16.25px;border-left:5px solid #dddddd}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#808080}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #dddddd;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:18px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:8.5px;margin:0 0 9px;font-size:12px;line-height:1.42857143;color:#444444;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#808080;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:18px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#fcfcfc}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:13.5px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:18px;font-size:19.5px;line-height:inherit;color:#777777;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:13px;line-height:1.42857143;color:#777777}.form-control{display:block;width:100%;height:36px;padding:8px 12px;font-size:13px;line-height:1.42857143;color:#777777;background-color:#ffffff;background-image:none;border:1px solid #dddddd;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#dddddd;opacity:1}.form-control:-ms-input-placeholder{color:#dddddd}.form-control::-webkit-input-placeholder{color:#dddddd}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#dddddd;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:36px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:53px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:18px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:31px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:30px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:53px;padding:14px 16px;font-size:17px;line-height:1.3333333;border-radius:6px}select.input-lg{height:53px;line-height:53px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:53px;padding:14px 16px;font-size:17px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:53px;line-height:53px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:53px;min-height:35px;padding:15px 16px;font-size:17px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:45px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:36px;height:36px;line-height:36px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:53px;height:53px;line-height:53px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:23px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#b7b7b7}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:17px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:13px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#474949;border-color:#474949}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#2e2f2f;border-color:#080808}.btn-default:hover{color:#ffffff;background-color:#2e2f2f;border-color:#292a2a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#2e2f2f;background-image:none;border-color:#292a2a}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#1c1d1d;border-color:#080808}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#474949;border-color:#474949}.btn-default .badge{color:#474949;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#d9230f;border-color:#d9230f}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#a91b0c;border-color:#621007}.btn-primary:hover{color:#ffffff;background-color:#a91b0c;border-color:#a01a0b}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#a91b0c;background-image:none;border-color:#a01a0b}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#881609;border-color:#621007}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#d9230f;border-color:#d9230f}.btn-primary .badge{color:#d9230f;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#469408;border-color:#469408}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#2f6405;border-color:#0d1b01}.btn-success:hover{color:#ffffff;background-color:#2f6405;border-color:#2b5a05}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#2f6405;background-image:none;border-color:#2b5a05}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#1f4204;border-color:#0d1b01}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#469408;border-color:#469408}.btn-success .badge{color:#469408;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#029acf;border-color:#029acf}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#02749c;border-color:#013c51}.btn-info:hover{color:#ffffff;background-color:#02749c;border-color:#016d92}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#02749c;background-image:none;border-color:#016d92}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#015a79;border-color:#013c51}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#029acf;border-color:#029acf}.btn-info .badge{color:#029acf;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#9b479f;border-color:#9b479f}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#79377c;border-color:#452047}.btn-warning:hover{color:#ffffff;background-color:#79377c;border-color:#723475}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#79377c;background-image:none;border-color:#723475}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#612c63;border-color:#452047}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#9b479f;border-color:#9b479f}.btn-warning .badge{color:#9b479f;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#d9831f;border-color:#d9831f}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#ac6819;border-color:#69400f}.btn-danger:hover{color:#ffffff;background-color:#ac6819;border-color:#a36317}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#ac6819;background-image:none;border-color:#a36317}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#8d5514;border-color:#69400f}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9831f;border-color:#d9831f}.btn-danger .badge{color:#d9831f;background-color:#ffffff}.btn-link{font-weight:400;color:#d9230f;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#91170a;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#808080;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:17px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:13px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:8px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#444444;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#d9230f}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#d9230f;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#808080}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#808080;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:53px;padding:14px 16px;font-size:17px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:53px;line-height:53px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:13px;font-weight:400;line-height:1;color:#777777;text-align:center;background-color:#dddddd;border:1px solid #dddddd;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:17px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#dddddd}.nav>li.disabled>a{color:#808080}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#808080;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#dddddd;border-color:#d9230f}.nav .nav-divider{height:1px;margin:8px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#dddddd #dddddd #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#777777;cursor:default;background-color:#fcfcfc;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fcfcfc}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#d9230f}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fcfcfc}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:40px;margin-bottom:18px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:40px;padding:11px 15px;font-size:17px;line-height:18px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:3px;margin-bottom:3px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:5.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:18px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:18px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:11px;padding-bottom:11px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:2px;margin-bottom:2px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:2px;margin-bottom:2px}.navbar-btn.btn-sm{margin-top:5px;margin-bottom:5px}.navbar-btn.btn-xs{margin-top:9px;margin-bottom:9px}.navbar-text{margin-top:11px;margin-bottom:11px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#ffffff;border-color:#eeeeee}.navbar-default .navbar-brand{color:#777777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#d9230f;background-color:transparent}.navbar-default .navbar-text{color:#777777}.navbar-default .navbar-nav>li>a{color:#777777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#d9230f;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#d9230f;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#d9230f;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#d9230f;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#d9230f;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#dddddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#dddddd}.navbar-default .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#eeeeee}.navbar-default .navbar-link{color:#777777}.navbar-default .navbar-link:hover{color:#d9230f}.navbar-default .btn-link{color:#777777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#d9230f}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#444444}.navbar-inverse{background-color:#d9230f;border-color:#a91b0c}.navbar-inverse .navbar-brand{color:#fac0ba}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#fac0ba}.navbar-inverse .navbar-nav>li>a{color:#fac0ba}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#a91b0c}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#a91b0c}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#fac0ba}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#a91b0c}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#a91b0c}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#b81e0d}.navbar-inverse .navbar-link{color:#fac0ba}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#fac0ba}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:18px;list-style:none;background-color:transparent;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#808080}.pagination{display:inline-block;padding-left:0;margin:18px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#444444;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:#d9230f;border-color:#d9230f}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#d9230f;border-color:#d9230f}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#dddddd;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:17px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:18px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#d9230f}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#dddddd;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#474949}.label-default[href]:hover,.label-default[href]:focus{background-color:#2e2f2f}.label-primary{background-color:#d9230f}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#a91b0c}.label-success{background-color:#469408}.label-success[href]:hover,.label-success[href]:focus{background-color:#2f6405}.label-info{background-color:#029acf}.label-info[href]:hover,.label-info[href]:focus{background-color:#02749c}.label-warning{background-color:#9b479f}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#79377c}.label-danger{background-color:#d9831f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#ac6819}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#d9230f;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#d9230f;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#f4f4f4}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:20px;font-weight:200}.jumbotron>hr{border-top-color:#dbdbdb}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:59px}}.thumbnail{display:block;padding:4px;margin-bottom:18px;line-height:1.42857143;background-color:#fcfcfc;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#d9230f}.thumbnail .caption{padding:9px;color:#777777}.alert{padding:15px;margin-bottom:18px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:18px;margin-bottom:18px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:18px;color:#ffffff;text-align:center;background-color:#d9230f;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#469408}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#029acf}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#9b479f}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9831f}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#808080;cursor:not-allowed;background-color:#dddddd}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#808080}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#d9230f;border-color:#d9230f}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#fac0ba}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:18px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:15px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#fcfcfc;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:18px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#444444;background-color:#fcfcfc;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#fcfcfc;background-color:#444444}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#d9230f}.panel-primary>.panel-heading{color:#ffffff;background-color:#d9230f;border-color:#d9230f}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d9230f}.panel-primary>.panel-heading .badge{color:#d9230f;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d9230f}.panel-success{border-color:#469408}.panel-success>.panel-heading{color:#ffffff;background-color:#469408;border-color:#469408}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#469408}.panel-success>.panel-heading .badge{color:#469408;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#469408}.panel-info{border-color:#029acf}.panel-info>.panel-heading{color:#ffffff;background-color:#029acf;border-color:#029acf}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#029acf}.panel-info>.panel-heading .badge{color:#029acf;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#029acf}.panel-warning{border-color:#9b479f}.panel-warning>.panel-heading{color:#ffffff;background-color:#9b479f;border-color:#9b479f}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#9b479f}.panel-warning>.panel-heading .badge{color:#9b479f;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#9b479f}.panel-danger{border-color:#d9831f}.panel-danger>.panel-heading{color:#ffffff;background-color:#d9831f;border-color:#d9831f}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d9831f}.panel-danger>.panel-heading .badge{color:#d9831f;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d9831f}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f4f4f4;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:19.5px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:13px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:13px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar-inverse .badge{background-color:#fff;color:#d9230f}.btn{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}.btn-default,.btn-default:hover{background-image:linear-gradient(#4f5151, #474949 6%, #3f4141);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4f5151', endColorstr='#ff3f4141', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #2e2f2f}.btn-primary,.btn-primary:hover{background-image:linear-gradient(#e72510, #d9230f 6%, #cb210e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe72510', endColorstr='#ffcb210e', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #a91b0c}.btn-success,.btn-success:hover{background-image:linear-gradient(#4da309, #469408 6%, #3f8507);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4da309', endColorstr='#ff3f8507', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #2f6405}.btn-info,.btn-info:hover{background-image:linear-gradient(#02a5de, #029acf 6%, #028fc0);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff02a5de', endColorstr='#ff028fc0', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #02749c}.btn-warning,.btn-warning:hover{background-image:linear-gradient(#a54caa, #9b479f 6%, #914294);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffa54caa', endColorstr='#ff914294', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #79377c}.btn-danger,.btn-danger:hover{background-image:linear-gradient(#e08b27, #d9831f 6%, #cc7b1d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe08b27', endColorstr='#ffcc7b1d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #ac6819}body{font-weight:200}th{color:#444444}legend{color:#444444}label{font-weight:normal}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#d9831f}.has-warning .form-control,.has-warning .form-control:focus{border-color:#d9831f}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#d9230f}.has-error .form-control,.has-error .form-control:focus{border-color:#d9230f}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#469408}.has-success .form-control,.has-success .form-control:focus{border-color:#469408}.pager a{color:#444444}.pager a:hover,.pager .active>a{border-color:#d9230f;color:#fff}.pager .disabled>a{border-color:#dddddd} diff --git a/themes/slate.css b/themes/slate.css new file mode 100644 index 00000000..6490098e --- /dev/null +++ b/themes/slate.css @@ -0,0 +1,11 @@ +/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#c8c8c8;background-color:#272b30}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#ffffff;text-decoration:none}a:hover,a:focus{color:#ffffff;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #1c1e22}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#7a8288}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#f89406}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#7a8288}.text-primary{color:#7a8288}a.text-primary:hover,a.text-primary:focus{color:#62686d}.text-success{color:#ffffff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-info{color:#ffffff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-warning{color:#ffffff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-danger{color:#ffffff}a.text-danger:hover,a.text-danger:focus{color:#e6e6e6}.bg-primary{color:#fff;background-color:#7a8288}a.bg-primary:hover,a.bg-primary:focus{background-color:#62686d}.bg-success{background-color:#62c462}a.bg-success:hover,a.bg-success:focus{background-color:#42b142}.bg-info{background-color:#5bc0de}a.bg-info:hover,a.bg-info:focus{background-color:#31b0d5}.bg-warning{background-color:#f89406}a.bg-warning:hover,a.bg-warning:focus{background-color:#c67605}.bg-danger{background-color:#ee5f5b}a.bg-danger:hover,a.bg-danger:focus{background-color:#e9322d}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #1c1e22}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #7a8288}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#7a8288}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #7a8288;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#3a3f44;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:#2e3338}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#7a8288;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #1c1e22}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #1c1e22}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #1c1e22}.table .table{background-color:#272b30}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #1c1e22}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #1c1e22}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#353a41}.table-hover>tbody>tr:hover{background-color:#49515a}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#49515a}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#3e444c}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#62c462}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#4fbd4f}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#5bc0de}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#46b8da}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f89406}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#df8505}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#ee5f5b}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ec4844}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #1c1e22}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#c8c8c8;border:0;border-bottom:1px solid #1c1e22}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#272b30}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#272b30;background-color:#ffffff;background-image:none;border:1px solid #000000;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#7a8288;opacity:1}.form-control:-ms-input-placeholder{color:#7a8288}.form-control::-webkit-input-placeholder{color:#7a8288}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#999999;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ffffff}.has-success .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ffffff;background-color:#62c462;border-color:#ffffff}.has-success .form-control-feedback{color:#ffffff}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ffffff}.has-warning .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ffffff;background-color:#f89406;border-color:#ffffff}.has-warning .form-control-feedback{color:#ffffff}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ffffff}.has-error .form-control{border-color:#ffffff;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#e6e6e6;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ffffff;background-color:#ee5f5b;border-color:#ffffff}.has-error .form-control-feedback{color:#ffffff}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#ffffff}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#3a3f44;border-color:#3a3f44}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#232628;border-color:#000000}.btn-default:hover{color:#ffffff;background-color:#232628;border-color:#1e2023}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#232628;background-image:none;border-color:#1e2023}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#121415;border-color:#000000}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#3a3f44;border-color:#3a3f44}.btn-default .badge{color:#3a3f44;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#7a8288;border-color:#7a8288}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#62686d;border-color:#3e4245}.btn-primary:hover{color:#ffffff;background-color:#62686d;border-color:#5d6368}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#62686d;background-image:none;border-color:#5d6368}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#51565a;border-color:#3e4245}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#7a8288;border-color:#7a8288}.btn-primary .badge{color:#7a8288;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#62c462;border-color:#62c462}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#42b142;border-color:#2d792d}.btn-success:hover{color:#ffffff;background-color:#42b142;border-color:#40a940}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#42b142;background-image:none;border-color:#40a940}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#399739;border-color:#2d792d}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#62c462;border-color:#62c462}.btn-success .badge{color:#62c462;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#5bc0de}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:#1f7e9a}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:#2aabd2}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#31b0d5;background-image:none;border-color:#2aabd2}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#269abc;border-color:#1f7e9a}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#5bc0de}.btn-info .badge{color:#5bc0de;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f89406;border-color:#f89406}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#c67605;border-color:#7c4a03}.btn-warning:hover{color:#ffffff;background-color:#c67605;border-color:#bc7005}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#c67605;background-image:none;border-color:#bc7005}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#a36104;border-color:#7c4a03}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f89406;border-color:#f89406}.btn-warning .badge{color:#f89406;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#e9322d;border-color:#b71713}.btn-danger:hover{color:#ffffff;background-color:#e9322d;border-color:#e82924}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#e9322d;background-image:none;border-color:#e82924}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#dc1c17;border-color:#b71713}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#ee5f5b;border-color:#ee5f5b}.btn-danger .badge{color:#ee5f5b;background-color:#ffffff}.btn-link{font-weight:400;color:#ffffff;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#ffffff;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#7a8288;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#3a3f44;background-clip:padding-box;border:1px solid #272b30;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#272b30}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#c8c8c8;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#272b30}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#272b30;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#7a8288}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#7a8288;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#272b30;text-align:center;background-color:#999999;border:1px solid rgba(0,0,0,0.6);border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#3e444c}.nav>li.disabled>a{color:#7a8288}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#7a8288;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#3e444c;border-color:#ffffff}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #1c1e22}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#1c1e22 #1c1e22 #1c1e22}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#ffffff;cursor:default;background-color:#3e444c;border:1px solid #1c1e22;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #1c1e22}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #1c1e22;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#272b30}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:transparent}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #1c1e22}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #1c1e22;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#272b30}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#3a3f44;border-color:#2b2e32}.navbar-default .navbar-brand{color:#c8c8c8}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#c8c8c8}.navbar-default .navbar-nav>li>a{color:#c8c8c8}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#272b2e}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#c8c8c8}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#272b2e}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#272b2e}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#272b2e}.navbar-default .navbar-toggle .icon-bar{background-color:#c8c8c8}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#2b2e32}.navbar-default .navbar-link{color:#c8c8c8}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#c8c8c8}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#7a8288;border-color:#62686d}.navbar-inverse .navbar-brand{color:#cccccc}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#cccccc}.navbar-inverse .navbar-nav>li>a{color:#cccccc}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#5d6368}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#62686d}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#62686d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#cccccc}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#5d6368}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#5d6368}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#5d6368}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#697075}.navbar-inverse .navbar-link{color:#cccccc}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#cccccc}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:transparent;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#7a8288}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#ffffff;text-decoration:none;background-color:#3a3f44;border:1px solid rgba(0,0,0,0.6)}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ffffff;background-color:transparent;border-color:rgba(0,0,0,0.6)}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#232628;border-color:rgba(0,0,0,0.6)}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#7a8288;cursor:not-allowed;background-color:#ffffff;border-color:rgba(0,0,0,0.6)}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#3a3f44;border:1px solid rgba(0,0,0,0.6);border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:transparent}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#7a8288;cursor:not-allowed;background-color:#3a3f44}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#3a3f44}.label-default[href]:hover,.label-default[href]:focus{background-color:#232628}.label-primary{background-color:#7a8288}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#62686d}.label-success{background-color:#62c462}.label-success[href]:hover,.label-success[href]:focus{background-color:#42b142}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f89406}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#c67605}.label-danger{background-color:#ee5f5b}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#e9322d}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#7a8288;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#ffffff;background-color:#7a8288}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#1c1e22}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#050506}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#ffffff}.thumbnail .caption{padding:9px;color:#c8c8c8}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#62c462;border-color:#62bd4f}.alert-success hr{border-top-color:#55b142}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#5bc0de;border-color:#3dced8}.alert-info hr{border-top-color:#2ac7d2}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#f89406;border-color:#e96506}.alert-warning hr{border-top-color:#d05a05}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#ee5f5b;border-color:#ed4d63}.alert-danger hr{border-top-color:#ea364f}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#1c1e22;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#7a8288;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#62c462}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f89406}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#ee5f5b}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#32383e;border:1px solid rgba(0,0,0,0.6)}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#7a8288;cursor:not-allowed;background-color:#999999}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#7a8288}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#a2aab4}a.list-group-item,button.list-group-item{color:#c8c8c8}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#ffffff}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#c8c8c8;text-decoration:none;background-color:#3e444c}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ffffff;background-color:#62c462}a.list-group-item-success,button.list-group-item-success{color:#ffffff}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ffffff;background-color:#4fbd4f}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-info{color:#ffffff;background-color:#5bc0de}a.list-group-item-info,button.list-group-item-info{color:#ffffff}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ffffff;background-color:#46b8da}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-warning{color:#ffffff;background-color:#f89406}a.list-group-item-warning,button.list-group-item-warning{color:#ffffff}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ffffff;background-color:#df8505}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-danger{color:#ffffff;background-color:#ee5f5b}a.list-group-item-danger,button.list-group-item-danger{color:#ffffff}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ffffff;background-color:#ec4844}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ffffff;border-color:#ffffff}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#2e3338;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#3e444c;border-top:1px solid rgba(0,0,0,0.6);border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #1c1e22}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid rgba(0,0,0,0.6)}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid rgba(0,0,0,0.6)}.panel-default{border-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading{color:#c8c8c8;background-color:#3e444c;border-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-default>.panel-heading .badge{color:#3e444c;background-color:#c8c8c8}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-primary{border-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading{color:#ffffff;background-color:#7a8288;border-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-primary>.panel-heading .badge{color:#7a8288;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-success{border-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading{color:#ffffff;background-color:#62c462;border-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-success>.panel-heading .badge{color:#62c462;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-info{border-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading{color:#ffffff;background-color:#5bc0de;border-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-warning{border-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading{color:#ffffff;background-color:#f89406;border-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-warning>.panel-heading .badge{color:#f89406;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.panel-danger{border-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading{color:#ffffff;background-color:#ee5f5b;border-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:rgba(0,0,0,0.6)}.panel-danger>.panel-heading .badge{color:#ee5f5b;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:rgba(0,0,0,0.6)}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#1c1e22;border:1px solid #0c0d0e;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#2e3338;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #1c1e22}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #1c1e22}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#2e3338;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#666666;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#2e3338;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#666666;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#2e3338;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#666666;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#2e3338}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#666666;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#2e3338}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#2e3338;border-bottom:1px solid #22262a;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.navbar .navbar-nav>li>a{border-right:1px solid rgba(0,0,0,0.2);border-left:1px solid rgba(255,255,255,0.1)}.navbar .navbar-nav>li>a:hover{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border-left-color:transparent}.navbar-inverse{background-image:linear-gradient(#8a9196, #7a8288 60%, #70787d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8a9196', endColorstr='#ff70787d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.navbar-inverse .badge{background-color:#5d6368}.navbar-inverse .navbar-nav>li>a:hover{background-image:linear-gradient(#404448, #4e5458 40%, #53595d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404448', endColorstr='#ff53595d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.navbar .nav .open>a{border-color:transparent}.navbar-nav>li.active>a{border-left-color:transparent}.navbar-form{margin-left:5px;margin-right:5px}.btn,.btn:hover{border-color:rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.btn-default{background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-default:hover{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary{background-image:linear-gradient(#8a9196, #7a8288 60%, #70787d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff8a9196', endColorstr='#ff70787d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-primary:hover{background-image:linear-gradient(#404448, #4e5458 40%, #53595d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff404448', endColorstr='#ff53595d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success{background-image:linear-gradient(#78cc78, #62c462 60%, #53be53);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff78cc78', endColorstr='#ff53be53', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-success:hover{background-image:linear-gradient(#2f7d2f, #379337 40%, #3a9a3a);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff2f7d2f', endColorstr='#ff3a9a3a', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info{background-image:linear-gradient(#74cae3, #5bc0de 60%, #4ab9db);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff74cae3', endColorstr='#ff4ab9db', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-info:hover{background-image:linear-gradient(#20829f, #2596b8 40%, #279dc1);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff20829f', endColorstr='#ff279dc1', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning{background-image:linear-gradient(#faa123, #f89406 60%, #e48806);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffaa123', endColorstr='#ffe48806', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-warning:hover{background-image:linear-gradient(#804d03, #9e5f04 40%, #a86404);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff804d03', endColorstr='#ffa86404', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger{background-image:linear-gradient(#f17a77, #ee5f5b 60%, #ec4d49);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff17a77', endColorstr='#ffec4d49', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-danger:hover{background-image:linear-gradient(#bb1813, #d71c16 40%, #e01d17);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffbb1813', endColorstr='#ffe01d17', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.btn-link,.btn-link:hover{border-color:transparent}h1,h2,h3,h4,h5,h6{text-shadow:-1px -1px 0 rgba(0,0,0,0.3)}.text-primary,.text-primary:hover{color:#7a8288}.text-success,.text-success:hover{color:#62c462}.text-danger,.text-danger:hover{color:#ee5f5b}.text-warning,.text-warning:hover{color:#f89406}.text-info,.text-info:hover{color:#5bc0de}.table .success,.table .warning,.table .danger,.table .info{color:#fff}.table-bordered tbody tr.success td,.table-bordered tbody tr.warning td,.table-bordered tbody tr.danger td,.table-bordered tbody tr.success:hover td,.table-bordered tbody tr.warning:hover td,.table-bordered tbody tr.danger:hover td{border-color:#1c1e22}.table-responsive>.table{background-color:#2e3338}input,textarea{color:#272b30}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f89406}.has-warning .form-control,.has-warning .form-control:focus{border-color:#f89406}.has-warning .input-group-addon{background-color:#3a3f44;border-color:rgba(0,0,0,0.6)}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#ee5f5b}.has-error .form-control,.has-error .form-control:focus{border-color:#ee5f5b}.has-error .input-group-addon{background-color:#3a3f44;border-color:rgba(0,0,0,0.6)}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#62c462}.has-success .form-control,.has-success .form-control:focus{border-color:#62c462}.has-success .input-group-addon{background-color:#3a3f44;border-color:rgba(0,0,0,0.6)}legend{color:#fff}.input-group-addon{background-color:#3a3f44;background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3);color:#ffffff}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:rgba(0,0,0,0.6)}.nav-pills>li>a{background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.nav-pills>li>a:hover{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills>li.active>a,.nav-pills>li.active>a:hover{background-color:none;background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid rgba(0,0,0,0.6)}.nav-pills>li.disabled>a,.nav-pills>li.disabled>a:hover{background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination>li>a,.pagination>li>span{text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination>li>a:hover,.pagination>li>span:hover{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination>li.active>a,.pagination>li.active>span{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pagination>li.disabled>a,.pagination>li.disabled>a:hover,.pagination>li.disabled>span,.pagination>li.disabled>span:hover{background-color:transparent;background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pager>li>a{background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;text-shadow:1px 1px 1px rgba(0,0,0,0.3)}.pager>li>a:hover{background-image:linear-gradient(#020202, #101112 40%, #141618);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff020202', endColorstr='#ff141618', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.pager>li.disabled>a,.pager>li.disabled>a:hover{background-color:transparent;background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.breadcrumb{border:1px solid rgba(0,0,0,0.6);text-shadow:1px 1px 1px rgba(0,0,0,0.3);background-image:linear-gradient(#484e55, #3a3f44 60%, #313539);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff484e55', endColorstr='#ff313539', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none}.alert .alert-link,.alert a{color:#fff;text-decoration:underline}.alert .close{color:#000000;text-decoration:none}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#0c0d0e}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border-color:rgba(0,0,0,0.6)}a.list-group-item-success.active{background-color:#62c462}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#4fbd4f}a.list-group-item-warning.active{background-color:#f89406}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#df8505}a.list-group-item-danger.active{background-color:#ee5f5b}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#ec4844}.jumbotron{border:1px solid rgba(0,0,0,0.6)}.panel-primary .panel-heading,.panel-success .panel-heading,.panel-danger .panel-heading,.panel-warning .panel-heading,.panel-info .panel-heading{border-color:#000} \ No newline at end of file diff --git a/themes/spacelab.css b/themes/spacelab.css new file mode 100644 index 00000000..8108a401 --- /dev/null +++ b/themes/spacelab.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#666666;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#3399f3;text-decoration:none}a:hover,a:focus{color:#3399f3;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:#2d2d2d}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#446e9b}a.text-primary:hover,a.text-primary:focus{color:#345578}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#446e9b}a.bg-primary:hover,a.bg-primary:focus{background-color:#345578}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#666666;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#666666}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#666666;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#a6a6a6}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#474949;border-color:#474949}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#2e2f2f;border-color:#080808}.btn-default:hover{color:#ffffff;background-color:#2e2f2f;border-color:#292a2a}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#2e2f2f;background-image:none;border-color:#292a2a}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#1c1d1d;border-color:#080808}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#474949;border-color:#474949}.btn-default .badge{color:#474949;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#446e9b;border-color:#446e9b}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#345578;border-color:#1d2f42}.btn-primary:hover{color:#ffffff;background-color:#345578;border-color:#315070}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#345578;background-image:none;border-color:#315070}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#2a435f;border-color:#1d2f42}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#446e9b;border-color:#446e9b}.btn-primary .badge{color:#446e9b;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#3cb521;border-color:#3cb521}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#2e8a19;border-color:#18490d}.btn-success:hover{color:#ffffff;background-color:#2e8a19;border-color:#2b8118}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#2e8a19;background-image:none;border-color:#2b8118}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#246c14;border-color:#18490d}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#3cb521;border-color:#3cb521}.btn-success .badge{color:#3cb521;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#3399f3;border-color:#3399f3}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#0e80e5;border-color:#09589d}.btn-info:hover{color:#ffffff;background-color:#0e80e5;border-color:#0d7bdc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#0e80e5;background-image:none;border-color:#0d7bdc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#0c6dc4;border-color:#09589d}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#3399f3;border-color:#3399f3}.btn-info .badge{color:#3399f3;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#d47500;border-color:#d47500}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#a15900;border-color:#552f00}.btn-warning:hover{color:#ffffff;background-color:#a15900;border-color:#975300}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#a15900;background-image:none;border-color:#975300}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#7d4500;border-color:#552f00}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#d47500;border-color:#d47500}.btn-warning .badge{color:#d47500;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#cd0200;border-color:#cd0200}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#9a0200;border-color:#4e0100}.btn-danger:hover{color:#ffffff;background-color:#9a0200;border-color:#900100}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#9a0200;background-image:none;border-color:#900100}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#760100;border-color:#4e0100}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#cd0200;border-color:#cd0200}.btn-danger .badge{color:#cd0200;background-color:#ffffff}.btn-link{font-weight:400;color:#3399f3;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#3399f3;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#446e9b}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#446e9b;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#666666;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#3399f3}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#666666;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#446e9b}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#eeeeee;border-color:#dddddd}.navbar-default .navbar-brand{color:#777777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#3399f3;background-color:transparent}.navbar-default .navbar-text{color:#777777}.navbar-default .navbar-nav>li>a{color:#777777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#3399f3;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#3399f3;background-color:transparent}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#3399f3;background-color:transparent}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#3399f3;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#3399f3;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#dddddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#dddddd}.navbar-default .navbar-toggle .icon-bar{background-color:#cccccc}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#dddddd}.navbar-default .navbar-link{color:#777777}.navbar-default .navbar-link:hover{color:#3399f3}.navbar-default .btn-link{color:#777777}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#3399f3}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#444444}.navbar-inverse{background-color:#446e9b;border-color:#345578}.navbar-inverse .navbar-brand{color:#dddddd}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#dddddd}.navbar-inverse .navbar-nav>li>a{color:#dddddd}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:transparent}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#345578}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#345578}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#dddddd}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#345578}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#345578}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#395c82}.navbar-inverse .navbar-link{color:#dddddd}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#dddddd}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#999999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#3399f3;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#3399f3;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#999999;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#474949}.label-default[href]:hover,.label-default[href]:focus{background-color:#2e2f2f}.label-primary{background-color:#446e9b}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#345578}.label-success{background-color:#3cb521}.label-success[href]:hover,.label-success[href]:focus{background-color:#2e8a19}.label-info{background-color:#3399f3}.label-info[href]:hover,.label-info[href]:focus{background-color:#0e80e5}.label-warning{background-color:#d47500}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#a15900}.label-danger{background-color:#cd0200}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#9a0200}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#3399f3;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#3399f3;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eeeeee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#3399f3}.thumbnail .caption{padding:9px;color:#666666}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#446e9b;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#3cb521}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#3399f3}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#d47500}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#cd0200}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#446e9b;border-color:#446e9b}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#c5d5e6}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#446e9b}.panel-primary>.panel-heading{color:#ffffff;background-color:#446e9b;border-color:#446e9b}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#446e9b}.panel-primary>.panel-heading .badge{color:#446e9b;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#446e9b}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#468847}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#3a87ad}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#fbeed5}.panel-warning>.panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#fbeed5}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#c09853}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger>.panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#eed3d7}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#b94a48}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#eed3d7}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{background-image:linear-gradient(#fff, #eee 50%, #e4e4e4);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe4e4e4', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #d5d5d5;text-shadow:0 1px 0 rgba(255,255,255,0.3)}.navbar-inverse{background-image:linear-gradient(#6d94bf, #446e9b 50%, #3e648d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6d94bf', endColorstr='#ff3e648d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #345578;text-shadow:0 -1px 0 rgba(0,0,0,0.3)}.navbar-inverse .badge{background-color:#fff;color:#446e9b}.navbar .badge{text-shadow:none}.navbar-nav>li>a,.navbar-nav>li>a:hover{padding-top:17px;padding-bottom:13px;transition:color ease-in-out .2s}.navbar-brand,.navbar-brand:hover{transition:color ease-in-out .2s}.navbar .caret,.navbar .caret:hover{transition:border-color ease-in-out .2s}.navbar .dropdown-menu{text-shadow:none}.btn{text-shadow:0 -1px 0 rgba(0,0,0,0.3)}.btn-default{background-image:linear-gradient(#6d7070, #474949 50%, #3d3f3f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6d7070', endColorstr='#ff3d3f3f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #2e2f2f}.btn-default:hover{background-image:linear-gradient(#636565, #3d3f3f 50%, #333434);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff636565', endColorstr='#ff333434', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #242525}.btn-primary{background-image:linear-gradient(#6d94bf, #446e9b 50%, #3e648d);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff6d94bf', endColorstr='#ff3e648d', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #345578}.btn-primary:hover{background-image:linear-gradient(#5f8ab9, #3e648d 50%, #385a7f);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5f8ab9', endColorstr='#ff385a7f', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #2e4b69}.btn-success{background-image:linear-gradient(#61dd45, #3cb521 50%, #36a41e);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff61dd45', endColorstr='#ff36a41e', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #2e8a19}.btn-success:hover{background-image:linear-gradient(#52da34, #36a41e 50%, #31921b);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff52da34', endColorstr='#ff31921b', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #287916}.btn-info{background-image:linear-gradient(#7bbdf7, #3399f3 50%, #208ff2);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff7bbdf7', endColorstr='#ff208ff2', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #0e80e5}.btn-info:hover{background-image:linear-gradient(#68b3f6, #208ff2 50%, #0e86ef);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff68b3f6', endColorstr='#ff0e86ef', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #0c75d2}.btn-warning{background-image:linear-gradient(#ff9c21, #d47500 50%, #c06a00);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff9c21', endColorstr='#ffc06a00', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #a15900}.btn-warning:hover{background-image:linear-gradient(#ff930d, #c06a00 50%, #ab5e00);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff930d', endColorstr='#ffab5e00', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #8d4e00}.btn-danger{background-image:linear-gradient(#ff1d1b, #cd0200 50%, #b90200);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff1d1b', endColorstr='#ffb90200', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #9a0200}.btn-danger:hover{background-image:linear-gradient(#ff0906, #b90200 50%, #a40200);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff0906', endColorstr='#ffa40200', GradientType=0);background-repeat:no-repeat;-webkit-filter:none;filter:none;border:1px solid #860100}.btn-link{text-shadow:none}.btn:active,.btn.active{background-image:none;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.panel-primary .panel-title{color:#fff} diff --git a/themes/superhero.css b/themes/superhero.css new file mode 100644 index 00000000..930a27fd --- /dev/null +++ b/themes/superhero.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Lato:300,400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.42857143;color:#ebebeb;background-color:#2b3e50}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#df691a;text-decoration:none}a:hover,a:focus{color:#df691a;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:0}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#2b3e50;border:1px solid #dddddd;border-radius:0;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #596a7b}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:400;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#ebebeb}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:80%}mark,.mark{padding:.2em;background-color:#f0ad4e}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#4e5d6c}.text-primary{color:#df691a}a.text-primary:hover,a.text-primary:focus{color:#b15315}.text-success{color:#ebebeb}a.text-success:hover,a.text-success:focus{color:#d2d2d2}.text-info{color:#ebebeb}a.text-info:hover,a.text-info:focus{color:#d2d2d2}.text-warning{color:#ebebeb}a.text-warning:hover,a.text-warning:focus{color:#d2d2d2}.text-danger{color:#ebebeb}a.text-danger:hover,a.text-danger:focus{color:#d2d2d2}.bg-primary{color:#fff;background-color:#df691a}a.bg-primary:hover,a.bg-primary:focus{background-color:#b15315}.bg-success{background-color:#5cb85c}a.bg-success:hover,a.bg-success:focus{background-color:#449d44}.bg-info{background-color:#5bc0de}a.bg-info:hover,a.bg-info:focus{background-color:#31b0d5}.bg-warning{background-color:#f0ad4e}a.bg-warning:hover,a.bg-warning:focus{background-color:#ec971f}.bg-danger{background-color:#d9534f}a.bg-danger:hover,a.bg-danger:focus{background-color:#c9302c}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid #ebebeb}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #4e5d6c}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#ebebeb}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #4e5d6c;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:0}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:0;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:0}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:6px;padding-bottom:6px;color:#4e5d6c;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:6px;line-height:1.42857143;vertical-align:top;border-top:1px solid #4e5d6c}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #4e5d6c}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #4e5d6c}.table .table{background-color:#2b3e50}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:3px}.table-bordered{border:1px solid #4e5d6c}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #4e5d6c}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#4e5d6c}.table-hover>tbody>tr:hover{background-color:#485563}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#485563}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#3d4954}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#5cb85c}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#4cae4c}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#5bc0de}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#46b8da}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#f0ad4e}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#eea236}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#d9534f}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#d43f3a}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #4e5d6c}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#ebebeb;border:0;border-bottom:1px solid #4e5d6c}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:15px;line-height:1.42857143;color:#2b3e50}.form-control{display:block;width:100%;height:39px;padding:8px 16px;font-size:15px;line-height:1.42857143;color:#2b3e50;background-color:#ffffff;background-image:none;border:1px solid transparent;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:transparent;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(0,0,0,0.6)}.form-control::-moz-placeholder{color:#cccccc;opacity:1}.form-control:-ms-input-placeholder{color:#cccccc}.form-control::-webkit-input-placeholder{color:#cccccc}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#ebebeb;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:39px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:52px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:0}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:0}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:33px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:52px;padding:12px 24px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-lg{height:52px;line-height:52px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:52px;padding:12px 24px;font-size:19px;line-height:1.3333333;border-radius:0}.form-group-lg select.form-control{height:52px;line-height:52px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:52px;min-height:40px;padding:13px 24px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:48.75px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:39px;height:39px;line-height:39px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:52px;height:52px;line-height:52px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#ebebeb}.has-success .form-control{border-color:#ebebeb;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#d2d2d2;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-success .input-group-addon{color:#ebebeb;background-color:#5cb85c;border-color:#ebebeb}.has-success .form-control-feedback{color:#ebebeb}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#ebebeb}.has-warning .form-control{border-color:#ebebeb;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#d2d2d2;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-warning .input-group-addon{color:#ebebeb;background-color:#f0ad4e;border-color:#ebebeb}.has-warning .form-control-feedback{color:#ebebeb}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#ebebeb}.has-error .form-control{border-color:#ebebeb;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#d2d2d2;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #fff}.has-error .input-group-addon{color:#ebebeb;background-color:#d9534f;border-color:#ebebeb}.has-error .form-control-feedback{color:#ebebeb}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#ffffff}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:30px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:13px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 16px;font-size:15px;line-height:1.42857143;border-radius:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#4e5d6c;border-color:transparent}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#39444e;border-color:rgba(0,0,0,0)}.btn-default:hover{color:#ffffff;background-color:#39444e;border-color:rgba(0,0,0,0)}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#39444e;background-image:none;border-color:rgba(0,0,0,0)}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#2a323a;border-color:rgba(0,0,0,0)}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#4e5d6c;border-color:transparent}.btn-default .badge{color:#4e5d6c;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#df691a;border-color:transparent}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#b15315;border-color:rgba(0,0,0,0)}.btn-primary:hover{color:#ffffff;background-color:#b15315;border-color:rgba(0,0,0,0)}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#b15315;background-image:none;border-color:rgba(0,0,0,0)}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#914411;border-color:rgba(0,0,0,0)}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#df691a;border-color:transparent}.btn-primary .badge{color:#df691a;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#5cb85c;border-color:transparent}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#449d44;border-color:rgba(0,0,0,0)}.btn-success:hover{color:#ffffff;background-color:#449d44;border-color:rgba(0,0,0,0)}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#449d44;background-image:none;border-color:rgba(0,0,0,0)}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#398439;border-color:rgba(0,0,0,0)}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#5cb85c;border-color:transparent}.btn-success .badge{color:#5cb85c;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:transparent}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:rgba(0,0,0,0)}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:rgba(0,0,0,0)}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#31b0d5;background-image:none;border-color:rgba(0,0,0,0)}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#269abc;border-color:rgba(0,0,0,0)}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:transparent}.btn-info .badge{color:#5bc0de;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#f0ad4e;border-color:transparent}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#ec971f;border-color:rgba(0,0,0,0)}.btn-warning:hover{color:#ffffff;background-color:#ec971f;border-color:rgba(0,0,0,0)}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#ec971f;background-image:none;border-color:rgba(0,0,0,0)}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#d58512;border-color:rgba(0,0,0,0)}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#f0ad4e;border-color:transparent}.btn-warning .badge{color:#f0ad4e;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#d9534f;border-color:transparent}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#c9302c;border-color:rgba(0,0,0,0)}.btn-danger:hover{color:#ffffff;background-color:#c9302c;border-color:rgba(0,0,0,0)}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#c9302c;background-image:none;border-color:rgba(0,0,0,0)}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#ac2925;border-color:rgba(0,0,0,0)}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#d9534f;border-color:transparent}.btn-danger .badge{color:#d9534f;background-color:#ffffff}.btn-link{font-weight:400;color:#df691a;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#df691a;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#4e5d6c;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:12px 24px;font-size:19px;line-height:1.3333333;border-radius:0}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:0}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:0}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#4e5d6c;background-clip:padding-box;border:1px solid transparent;border-radius:0;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#2b3e50}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#ebebeb;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ebebeb;text-decoration:none;background-color:#485563}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#df691a;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#2b3e50}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#2b3e50;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:52px;padding:12px 24px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:52px;line-height:52px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:0}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 16px;font-size:15px;font-weight:400;line-height:1;color:#2b3e50;text-align:center;background-color:#4e5d6c;border:1px solid transparent;border-radius:0}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:0}.input-group-addon.input-lg{padding:12px 24px;font-size:19px;border-radius:0}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#4e5d6c}.nav>li.disabled>a{color:#4e5d6c}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#4e5d6c;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#4e5d6c;border-color:#df691a}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid transparent}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:0 0 0 0}.nav-tabs>li>a:hover{border-color:#4e5d6c #4e5d6c transparent}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#ebebeb;cursor:default;background-color:#2b3e50;border:1px solid #4e5d6c;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #4e5d6c}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #4e5d6c;border-radius:0 0 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#4e5d6c}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#df691a}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #4e5d6c}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #4e5d6c;border-radius:0 0 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#4e5d6c}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:40px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:0}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:40px;padding:9.5px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:3px;margin-bottom:3px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:0}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:4.75px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:9.5px;padding-bottom:9.5px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:0.5px;margin-bottom:0.5px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:0.5px;margin-bottom:0.5px}.navbar-btn.btn-sm{margin-top:5px;margin-bottom:5px}.navbar-btn.btn-xs{margin-top:9px;margin-bottom:9px}.navbar-text{margin-top:9.5px;margin-bottom:9.5px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#4e5d6c;border-color:transparent}.navbar-default .navbar-brand{color:#ebebeb}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ebebeb;background-color:transparent}.navbar-default .navbar-text{color:#ebebeb}.navbar-default .navbar-nav>li>a{color:#ebebeb}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ebebeb;background-color:#485563}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ebebeb;background-color:#485563}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ebebeb;background-color:#485563}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ebebeb}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ebebeb;background-color:#485563}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ebebeb;background-color:#485563}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#485563}.navbar-default .navbar-toggle .icon-bar{background-color:#ebebeb}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:transparent}.navbar-default .navbar-link{color:#ebebeb}.navbar-default .navbar-link:hover{color:#ebebeb}.navbar-default .btn-link{color:#ebebeb}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ebebeb}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#df691a;border-color:transparent}.navbar-inverse .navbar-brand{color:#ebebeb}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ebebeb;background-color:transparent}.navbar-inverse .navbar-text{color:#ebebeb}.navbar-inverse .navbar-nav>li>a{color:#ebebeb}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ebebeb;background-color:#c85e17}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ebebeb;background-color:#c85e17}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ebebeb;background-color:#c85e17}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ebebeb}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ebebeb;background-color:#c85e17}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ebebeb;background-color:#c85e17}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#c85e17}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ebebeb}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#bf5a16}.navbar-inverse .navbar-link{color:#ebebeb}.navbar-inverse .navbar-link:hover{color:#ebebeb}.navbar-inverse .btn-link{color:#ebebeb}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ebebeb}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444444}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#4e5d6c;border-radius:0}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ebebeb;content:"/\00a0"}.breadcrumb>.active{color:#ebebeb}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:0}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 16px;margin-left:-1px;line-height:1.42857143;color:#ebebeb;text-decoration:none;background-color:#4e5d6c;border:1px solid transparent}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ebebeb;background-color:#485563;border-color:transparent}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ebebeb;cursor:default;background-color:#df691a;border-color:transparent}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#323c46;cursor:not-allowed;background-color:#4e5d6c;border-color:transparent}.pagination-lg>li>a,.pagination-lg>li>span{padding:12px 24px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#4e5d6c;border:1px solid transparent;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#485563}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#323c46;cursor:not-allowed;background-color:#4e5d6c}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#4e5d6c}.label-default[href]:hover,.label-default[href]:focus{background-color:#39444e}.label-primary{background-color:#df691a}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#b15315}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:300;line-height:1;color:#ebebeb;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#4e5d6c;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#df691a;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#4e5d6c}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#39444e}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:0}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.42857143;background-color:#2b3e50;border:1px solid #dddddd;border-radius:0;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#df691a}.thumbnail .caption{padding:9px;color:#ebebeb}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:0}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ebebeb;background-color:#5cb85c;border-color:transparent}.alert-success hr{border-top-color:rgba(0,0,0,0)}.alert-success .alert-link{color:#d2d2d2}.alert-info{color:#ebebeb;background-color:#5bc0de;border-color:transparent}.alert-info hr{border-top-color:rgba(0,0,0,0)}.alert-info .alert-link{color:#d2d2d2}.alert-warning{color:#ebebeb;background-color:#f0ad4e;border-color:transparent}.alert-warning hr{border-top-color:rgba(0,0,0,0)}.alert-warning .alert-link{color:#d2d2d2}.alert-danger{color:#ebebeb;background-color:#d9534f;border-color:transparent}.alert-danger hr{border-top-color:rgba(0,0,0,0)}.alert-danger .alert-link{color:#d2d2d2}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#4e5d6c;border-radius:0;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:21px;color:#ffffff;text-align:center;background-color:#df691a;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#4e5d6c;border:1px solid transparent}.list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#4e5d6c;cursor:not-allowed;background-color:#ebebeb}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#4e5d6c}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#df691a;border-color:#df691a}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#f9decc}a.list-group-item,button.list-group-item{color:#ebebeb}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#ebebeb}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#ebebeb;text-decoration:none;background-color:#485563}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#ebebeb;background-color:#5cb85c}a.list-group-item-success,button.list-group-item-success{color:#ebebeb}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#ebebeb;background-color:#4cae4c}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#ebebeb;border-color:#ebebeb}.list-group-item-info{color:#ebebeb;background-color:#5bc0de}a.list-group-item-info,button.list-group-item-info{color:#ebebeb}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#ebebeb;background-color:#46b8da}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#ebebeb;border-color:#ebebeb}.list-group-item-warning{color:#ebebeb;background-color:#f0ad4e}a.list-group-item-warning,button.list-group-item-warning{color:#ebebeb}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#ebebeb;background-color:#eea236}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#ebebeb;border-color:#ebebeb}.list-group-item-danger{color:#ebebeb;background-color:#d9534f}a.list-group-item-danger,button.list-group-item-danger{color:#ebebeb}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#ebebeb;background-color:#d43f3a}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#ebebeb;border-color:#ebebeb}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#4e5d6c;border:1px solid transparent;border-radius:0;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:-1;border-top-right-radius:-1}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#485563;border-top:1px solid transparent;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:-1;border-top-right-radius:-1}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:-1}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:-1}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #4e5d6c}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:0}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid transparent}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid transparent}.panel-default{border-color:transparent}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:transparent}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-primary{border-color:transparent}.panel-primary>.panel-heading{color:#ffffff;background-color:#df691a;border-color:transparent}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-primary>.panel-heading .badge{color:#df691a;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-success{border-color:transparent}.panel-success>.panel-heading{color:#ebebeb;background-color:#5cb85c;border-color:transparent}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-success>.panel-heading .badge{color:#5cb85c;background-color:#ebebeb}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-info{border-color:transparent}.panel-info>.panel-heading{color:#ebebeb;background-color:#5bc0de;border-color:transparent}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ebebeb}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-warning{border-color:transparent}.panel-warning>.panel-heading{color:#ebebeb;background-color:#f0ad4e;border-color:transparent}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-warning>.panel-heading .badge{color:#f0ad4e;background-color:#ebebeb}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.panel-danger{border-color:transparent}.panel-danger>.panel-heading{color:#ebebeb;background-color:#d9534f;border-color:transparent}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:transparent}.panel-danger>.panel-heading .badge{color:#d9534f;background-color:#ebebeb}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:transparent}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#4e5d6c;border:1px solid transparent;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:0}.well-sm{padding:9px;border-radius:0}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#ebebeb;text-shadow:none;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ebebeb;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#4e5d6c;background-clip:padding-box;border:1px solid transparent;border-radius:0;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #2b3e50}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #2b3e50}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:0}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Lato","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#4e5d6c;background-clip:padding-box;border:1px solid transparent;border-radius:0;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:transparent;border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#4e5d6c;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:transparent;border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#4e5d6c;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:transparent}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#4e5d6c}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:transparent}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#4e5d6c}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#485563;border-bottom:1px solid #3d4954;border-radius:-1 -1 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{box-shadow:none;border:none;font-size:12px}.navbar-default .badge{background-color:#fff;color:#4e5d6c}.navbar-inverse .badge{background-color:#fff;color:#df691a}.btn-default:hover{background-color:#485563}.btn-sm,.btn-xs{font-size:12px}.text-primary,.text-primary:hover{color:#df691a}.text-success,.text-success:hover{color:#5cb85c}.text-danger,.text-danger:hover{color:#d9534f}.text-warning,.text-warning:hover{color:#f0ad4e}.text-info,.text-info:hover{color:#5bc0de}.page-header{border-bottom-color:#4e5d6c}.dropdown-menu{border:none;margin:0;box-shadow:none}.dropdown-menu>li>a{font-size:12px}.btn-group.open .dropdown-toggle{box-shadow:none}.dropdown-header{font-size:12px}table,.table{font-size:12px}table a:not(.btn),.table a:not(.btn){color:#fff;text-decoration:underline}table .dropdown-menu a,.table .dropdown-menu a{text-decoration:none}table .text-muted,.table .text-muted{color:#4e5d6c}table>thead>tr>th,.table>thead>tr>th,table>tbody>tr>th,.table>tbody>tr>th,table>tfoot>tr>th,.table>tfoot>tr>th,table>thead>tr>td,.table>thead>tr>td,table>tbody>tr>td,.table>tbody>tr>td,table>tfoot>tr>td,.table>tfoot>tr>td{border-color:transparent}input,textarea{color:#2b3e50}label,.radio label,.checkbox label,.help-block{font-size:12px}.input-addon,.input-group-addon{color:#ebebeb}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label,.has-warning .form-control-feedback{color:#f0ad4e}.has-warning .form-control,.has-warning .form-control:focus{border:4px solid #f0ad4e}.has-warning .input-group-addon{border:none}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label,.has-error .form-control-feedback{color:#d9534f}.has-error .form-control,.has-error .form-control:focus{border:4px solid #d9534f}.has-error .input-group-addon{border:none}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label,.has-success .form-control-feedback{color:#5cb85c}.has-success .form-control,.has-success .form-control:focus{border:4px solid #5cb85c}.has-success .input-group-addon{border:none}.form-control:focus{box-shadow:none}.has-warning .form-control:focus,.has-error .form-control:focus,.has-success .form-control:focus{box-shadow:none}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.nav-tabs>li>a{color:#ebebeb}.nav-pills>li>a{color:#ebebeb}.pager a{color:#ebebeb}.alert{color:#fff}.alert a,.alert .alert-link{color:#fff}.close{opacity:0.4}.close:hover,.close:focus{opacity:1}.well{box-shadow:none}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{border:none}a.list-group-item-success.active{background-color:#5cb85c}a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{background-color:#4cae4c}a.list-group-item-warning.active{background-color:#f0ad4e}a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{background-color:#eea236}a.list-group-item-danger.active{background-color:#d9534f}a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{background-color:#d43f3a}.panel{border:none}.panel-default>.panel-heading{background-color:#485563;color:#ebebeb}.thumbnail{background-color:#4e5d6c;border:none}.modal{padding:0}.modal-header,.modal-footer{background-color:#485563;border:none;border-radius:0}.popover-title{border:none} diff --git a/themes/united.css b/themes/united.css new file mode 100644 index 00000000..445601ec --- /dev/null +++ b/themes/united.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Ubuntu:400,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Ubuntu",Tahoma,"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333333;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#e95420;text-decoration:none}a:hover,a:focus{color:#ac3911;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eeeeee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Ubuntu",Tahoma,"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#aea79f}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#aea79f}.text-primary{color:#e95420}a.text-primary:hover,a.text-primary:focus{color:#c34113}.text-success{color:#468847}a.text-success:hover,a.text-success:focus{color:#356635}.text-info{color:#3a87ad}a.text-info:hover,a.text-info:focus{color:#2d6987}.text-warning{color:#c09853}a.text-warning:hover,a.text-warning:focus{color:#a47e3c}.text-danger{color:#b94a48}a.text-danger:hover,a.text-danger:focus{color:#953b39}.bg-primary{color:#fff;background-color:#e95420}a.bg-primary:hover,a.bg-primary:focus{background-color:#c34113}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eeeeee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eeeeee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#aea79f}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eeeeee;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#aea79f;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:14px;line-height:1.42857143;color:#333333}.form-control{display:block;width:100%;height:38px;padding:8px 12px;font-size:14px;line-height:1.42857143;color:#333333;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#aea79f;opacity:1}.form-control:-ms-input-placeholder{color:#aea79f}.form-control::-webkit-input-placeholder{color:#aea79f}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:38px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:30px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:54px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:54px;line-height:54px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:54px;line-height:54px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:54px;min-height:38px;padding:15px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:47.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:38px;height:38px;line-height:38px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:54px;height:54px;line-height:54px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#468847}.has-success .form-control{border-color:#468847;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#356635;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #7aba7b}.has-success .input-group-addon{color:#468847;background-color:#dff0d8;border-color:#468847}.has-success .form-control-feedback{color:#468847}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#c09853}.has-warning .form-control{border-color:#c09853;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#a47e3c;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #dbc59e}.has-warning .input-group-addon{color:#c09853;background-color:#fcf8e3;border-color:#c09853}.has-warning .form-control-feedback{color:#c09853}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#b94a48}.has-error .form-control{border-color:#b94a48;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#953b39;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #d59392}.has-error .input-group-addon{color:#b94a48;background-color:#f2dede;border-color:#b94a48}.has-error .form-control-feedback{color:#b94a48}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:29px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:15px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#ffffff;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#ffffff;background-color:#aea79f;border-color:#aea79f}.btn-default:focus,.btn-default.focus{color:#ffffff;background-color:#978e83;border-color:#6f675e}.btn-default:hover{color:#ffffff;background-color:#978e83;border-color:#92897e}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#ffffff;background-color:#978e83;background-image:none;border-color:#92897e}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#ffffff;background-color:#867c71;border-color:#6f675e}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#aea79f;border-color:#aea79f}.btn-default .badge{color:#aea79f;background-color:#ffffff}.btn-primary{color:#ffffff;background-color:#e95420;border-color:#e95420}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#c34113;border-color:#7d2a0c}.btn-primary:hover{color:#ffffff;background-color:#c34113;border-color:#b93e12}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#c34113;background-image:none;border-color:#b93e12}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#a23610;border-color:#7d2a0c}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#e95420;border-color:#e95420}.btn-primary .badge{color:#e95420;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#38b44a;border-color:#38b44a}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#2c8d3a;border-color:#1a5322}.btn-success:hover{color:#ffffff;background-color:#2c8d3a;border-color:#298537}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#2c8d3a;background-image:none;border-color:#298537}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#23722f;border-color:#1a5322}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#38b44a;border-color:#38b44a}.btn-success .badge{color:#38b44a;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#772953;border-color:#772953}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#511c39;border-color:#180811}.btn-info:hover{color:#ffffff;background-color:#511c39;border-color:#491933}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#511c39;background-image:none;border-color:#491933}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#371326;border-color:#180811}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#772953;border-color:#772953}.btn-info .badge{color:#772953;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#efb73e;border-color:#efb73e}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#e7a413;border-color:#a0720d}.btn-warning:hover{color:#ffffff;background-color:#e7a413;border-color:#dd9d12}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#e7a413;background-image:none;border-color:#dd9d12}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#c68c10;border-color:#a0720d}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#efb73e;border-color:#efb73e}.btn-warning .badge{color:#efb73e;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#df382c;border-color:#df382c}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#bc271c;border-color:#791912}.btn-danger:hover{color:#ffffff;background-color:#bc271c;border-color:#b3251b}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#bc271c;background-image:none;border-color:#b3251b}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#9d2118;border-color:#791912}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#df382c;border-color:#df382c}.btn-danger .badge{color:#df382c;background-color:#ffffff}.btn-link{font-weight:400;color:#e95420;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#ac3911;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#aea79f;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:4px;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#ffffff;text-decoration:none;background-color:#e95420}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#e95420;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#aea79f}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#aea79f;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:54px;padding:14px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:54px;line-height:54px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:14px;font-weight:400;line-height:1;color:#333333;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:14px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#aea79f}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#aea79f;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#e95420}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#777777;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#e95420}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:6px;margin-bottom:6px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:6px;margin-bottom:6px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#e95420;border-color:#d34615}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#ac3911}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#c34113}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#c34113}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#ac3911}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#c34113}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ac3911}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ac3911}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#d34615}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#772953;border-color:#511c39}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:none}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#3e152b}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#511c39}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#511c39}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#511c39}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#511c39}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#3e152b}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#511c39}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#3e152b}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#3e152b}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#5c2040}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#cccccc}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#cccccc;content:"/\00a0"}.breadcrumb>.active{color:#aea79f}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.42857143;color:#e95420;text-decoration:none;background-color:#ffffff;border:1px solid #dddddd}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#ac3911;background-color:#eeeeee;border-color:#dddddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#aea79f;cursor:default;background-color:#f5f5f5;border-color:#dddddd}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#aea79f;cursor:not-allowed;background-color:#ffffff;border-color:#dddddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:14px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#ffffff;border:1px solid #dddddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#aea79f;cursor:not-allowed;background-color:#ffffff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#aea79f}.label-default[href]:hover,.label-default[href]:focus{background-color:#978e83}.label-primary{background-color:#e95420}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#c34113}.label-success{background-color:#38b44a}.label-success[href]:hover,.label-success[href]:focus{background-color:#2c8d3a}.label-info{background-color:#772953}.label-info[href]:hover,.label-info[href]:focus{background-color:#511c39}.label-warning{background-color:#efb73e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#e7a413}.label-danger{background-color:#df382c}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#bc271c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#aea79f;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#e95420;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eeeeee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#ffffff;border:1px solid #dddddd;border-radius:4px;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#e95420}.thumbnail .caption{padding:9px;color:#333333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#356635}.alert-info{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#2d6987}.alert-warning{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.alert-warning hr{border-top-color:#f8e5be}.alert-warning .alert-link{color:#a47e3c}.alert-danger{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.alert-danger hr{border-top-color:#e6c1c7}.alert-danger .alert-link{color:#953b39}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#ffffff;text-align:center;background-color:#e95420;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#38b44a}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#772953}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#efb73e}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#df382c}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#aea79f;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#aea79f}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#e95420;border-color:#e95420}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#fbe2da}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#468847;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#468847}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#468847;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#468847;border-color:#468847}.list-group-item-info{color:#3a87ad;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#3a87ad}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#3a87ad;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#3a87ad;border-color:#3a87ad}.list-group-item-warning{color:#c09853;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#c09853}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#c09853;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#c09853;border-color:#c09853}.list-group-item-danger{color:#b94a48;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#b94a48}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#b94a48;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#b94a48;border-color:#b94a48}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#ffffff;border:1px solid transparent;border-radius:4px;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#e95420}.panel-primary>.panel-heading{color:#ffffff;background-color:#e95420;border-color:#e95420}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#e95420}.panel-primary>.panel-heading .badge{color:#e95420;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#e95420}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#468847;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#468847}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#3a87ad;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#3a87ad}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#fbeed5}.panel-warning>.panel-heading{color:#c09853;background-color:#fcf8e3;border-color:#fbeed5}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#fbeed5}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#c09853}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#fbeed5}.panel-danger{border-color:#eed3d7}.panel-danger>.panel-heading{color:#b94a48;background-color:#f2dede;border-color:#eed3d7}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#eed3d7}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#b94a48}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#eed3d7}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:bold;line-height:1;color:#000000;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#000000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Ubuntu",Tahoma,"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#000000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Ubuntu",Tahoma,"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#ffffff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#ffffff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999999;border-bottom-color:rgba(0,0,0,0.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#ffffff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999999;border-left-color:rgba(0,0,0,0.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#ffffff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar-default .badge{background-color:#fff;color:#e95420}.navbar-inverse .badge{background-color:#fff;color:#772953}@media (max-width:767px){.navbar .dropdown-menu a{color:#fff}} diff --git a/themes/yeti.css b/themes/yeti.css new file mode 100644 index 00000000..7c0e8dd8 --- /dev/null +++ b/themes/yeti.css @@ -0,0 +1,11 @@ +@import url("https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,700italic,400,300,700&display=swap");/*! + * bootswatch v3.4.1 + * Homepage: https://bootswatch.com + * Copyright 2012-2019 Thomas Park + * Licensed under MIT + * Based on Bootstrap +*//*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{color:#000 !important;text-shadow:none !important;background:transparent !important;box-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:"Glyphicons Halflings";src:url("../fonts/glyphicons-halflings-regular.eot");src:url("../fonts/glyphicons-halflings-regular.eot?#iefix") format("embedded-opentype"),url("../fonts/glyphicons-halflings-regular.woff2") format("woff2"),url("../fonts/glyphicons-halflings-regular.woff") format("woff"),url("../fonts/glyphicons-halflings-regular.ttf") format("truetype"),url("../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular") format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{box-sizing:border-box}*:before,*:after{box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;color:#222222;background-color:#ffffff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#008cba;text-decoration:none}a:hover,a:focus{color:#008cba;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:0}.img-thumbnail{padding:4px;line-height:1.4;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:21px;margin-bottom:21px;border:0;border-top:1px solid #dddddd}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role="button"]{cursor:pointer}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:300;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999999}h1,.h1,h2,.h2,h3,.h3{margin-top:21px;margin-bottom:10.5px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10.5px;margin-bottom:10.5px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:39px}h2,.h2{font-size:32px}h3,.h3{font-size:26px}h4,.h4{font-size:19px}h5,.h5{font-size:15px}h6,.h6{font-size:13px}p{margin:0 0 10.5px}.lead{margin-bottom:21px;font-size:17px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:22.5px}}small,.small{font-size:80%}mark,.mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#999999}.text-primary{color:#008cba}a.text-primary:hover,a.text-primary:focus{color:#006687}.text-success{color:#43ac6a}a.text-success:hover,a.text-success:focus{color:#358753}.text-info{color:#5bc0de}a.text-info:hover,a.text-info:focus{color:#31b0d5}.text-warning{color:#e99002}a.text-warning:hover,a.text-warning:focus{color:#b67102}.text-danger{color:#f04124}a.text-danger:hover,a.text-danger:focus{color:#d32a0e}.bg-primary{color:#fff;background-color:#008cba}a.bg-primary:hover,a.bg-primary:focus{background-color:#006687}.bg-success{background-color:#dff0d8}a.bg-success:hover,a.bg-success:focus{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover,a.bg-info:focus{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover,a.bg-warning:focus{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover,a.bg-danger:focus{background-color:#e4b9b9}.page-header{padding-bottom:9.5px;margin:42px 0 21px;border-bottom:1px solid #dddddd}ul,ol{margin-top:0;margin-bottom:10.5px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:21px}dt,dd{line-height:1.4}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10.5px 21px;margin:0 0 21px;font-size:18.75px;border-left:5px solid #dddddd}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.4;color:#6f6f6f}blockquote footer:before,blockquote small:before,blockquote .small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #dddddd;border-left:0}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:""}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:"\00A0 \2014"}address{margin-bottom:21px;font-style:normal;line-height:1.4}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:0}kbd{padding:2px 4px;font-size:90%;color:#ffffff;background-color:#333333;border-radius:0;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;box-shadow:none}pre{display:block;padding:10px;margin:0 0 10.5px;font-size:14px;line-height:1.4;color:#333333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #cccccc;border-radius:0}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*="col-"]{padding-right:0;padding-left:0}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0%}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0%}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0%}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0%}}table{background-color:transparent}table col[class*="col-"]{position:static;display:table-column;float:none}table td[class*="col-"],table th[class*="col-"]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#999999;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:21px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.4;vertical-align:top;border-top:1px solid #dddddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #dddddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #dddddd}.table .table{background-color:#ffffff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #dddddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15.75px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #dddddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:21px;font-size:22.5px;line-height:inherit;color:#333333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type="search"]{box-sizing:border-box;-webkit-appearance:none;appearance:none}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:9px;font-size:15px;line-height:1.4;color:#6f6f6f}.form-control{display:block;width:100%;height:39px;padding:8px 12px;font-size:15px;line-height:1.4;color:#6f6f6f;background-color:#ffffff;background-image:none;border:1px solid #cccccc;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 8px rgba(102,175,233,0.6)}.form-control::-moz-placeholder{color:#999999;opacity:1}.form-control:-ms-input-placeholder{color:#999999}.form-control::-webkit-input-placeholder{color:#999999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eeeeee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type="date"].form-control,input[type="time"].form-control,input[type="datetime-local"].form-control,input[type="month"].form-control{line-height:39px}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm,.input-group-sm input[type="date"],.input-group-sm input[type="time"],.input-group-sm input[type="datetime-local"],.input-group-sm input[type="month"]{line-height:36px}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg,.input-group-lg input[type="date"],.input-group-lg input[type="time"],.input-group-lg input[type="datetime-local"],.input-group-lg input[type="month"]{line-height:60px}}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.radio label,.checkbox label{min-height:21px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-top:4px \9;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:36px;padding-top:9px;padding-bottom:9px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}select.input-sm{height:36px;line-height:36px}textarea.input-sm,select[multiple].input-sm{height:auto}.form-group-sm .form-control{height:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}.form-group-sm select.form-control{height:36px;line-height:36px}.form-group-sm textarea.form-control,.form-group-sm select[multiple].form-control{height:auto}.form-group-sm .form-control-static{height:36px;min-height:33px;padding:9px 12px;font-size:12px;line-height:1.5}.input-lg{height:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-lg{height:60px;line-height:60px}textarea.input-lg,select[multiple].input-lg{height:auto}.form-group-lg .form-control{height:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}.form-group-lg select.form-control{height:60px;line-height:60px}.form-group-lg textarea.form-control,.form-group-lg select[multiple].form-control{height:auto}.form-group-lg .form-control-static{height:60px;min-height:40px;padding:17px 20px;font-size:19px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:48.75px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:39px;height:39px;line-height:39px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback,.input-group-lg+.form-control-feedback,.form-group-lg .form-control+.form-control-feedback{width:60px;height:60px;line-height:60px}.input-sm+.form-control-feedback,.input-group-sm+.form-control-feedback,.form-group-sm .form-control+.form-control-feedback{width:36px;height:36px;line-height:36px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#43ac6a}.has-success .form-control{border-color:#43ac6a;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#358753;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #85d0a1}.has-success .input-group-addon{color:#43ac6a;background-color:#dff0d8;border-color:#43ac6a}.has-success .form-control-feedback{color:#43ac6a}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#e99002}.has-warning .form-control{border-color:#e99002;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#b67102;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #febc53}.has-warning .input-group-addon{color:#e99002;background-color:#fcf8e3;border-color:#e99002}.has-warning .form-control-feedback{color:#e99002}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#f04124}.has-error .form-control{border-color:#f04124;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#d32a0e;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #f79483}.has-error .input-group-addon{color:#f04124;background-color:#f2dede;border-color:#f04124}.has-error .form-control-feedback{color:#f04124}.has-feedback label~.form-control-feedback{top:26px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#626262}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{padding-top:9px;margin-top:0;margin-bottom:0}.form-horizontal .radio,.form-horizontal .checkbox{min-height:30px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:9px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:17px;font-size:19px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:9px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:8px 12px;font-size:15px;line-height:1.4;border-radius:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333333;text-decoration:none}.btn:active,.btn.active{background-image:none;outline:0;box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:0.65;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333333;background-color:#e7e7e7;border-color:#cccccc}.btn-default:focus,.btn-default.focus{color:#333333;background-color:#cecece;border-color:#8c8c8c}.btn-default:hover{color:#333333;background-color:#cecece;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333333;background-color:#cecece;background-image:none;border-color:#adadad}.btn-default:active:hover,.btn-default.active:hover,.open>.dropdown-toggle.btn-default:hover,.btn-default:active:focus,.btn-default.active:focus,.open>.dropdown-toggle.btn-default:focus,.btn-default:active.focus,.btn-default.active.focus,.open>.dropdown-toggle.btn-default.focus{color:#333333;background-color:#bcbcbc;border-color:#8c8c8c}.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus{background-color:#e7e7e7;border-color:#cccccc}.btn-default .badge{color:#e7e7e7;background-color:#333333}.btn-primary{color:#ffffff;background-color:#008cba;border-color:#0079a1}.btn-primary:focus,.btn-primary.focus{color:#ffffff;background-color:#006687;border-color:#001921}.btn-primary:hover{color:#ffffff;background-color:#006687;border-color:#004b63}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#ffffff;background-color:#006687;background-image:none;border-color:#004b63}.btn-primary:active:hover,.btn-primary.active:hover,.open>.dropdown-toggle.btn-primary:hover,.btn-primary:active:focus,.btn-primary.active:focus,.open>.dropdown-toggle.btn-primary:focus,.btn-primary:active.focus,.btn-primary.active.focus,.open>.dropdown-toggle.btn-primary.focus{color:#ffffff;background-color:#004b63;border-color:#001921}.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus{background-color:#008cba;border-color:#0079a1}.btn-primary .badge{color:#008cba;background-color:#ffffff}.btn-success{color:#ffffff;background-color:#43ac6a;border-color:#3c9a5f}.btn-success:focus,.btn-success.focus{color:#ffffff;background-color:#358753;border-color:#183e26}.btn-success:hover{color:#ffffff;background-color:#358753;border-color:#2b6e44}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#ffffff;background-color:#358753;background-image:none;border-color:#2b6e44}.btn-success:active:hover,.btn-success.active:hover,.open>.dropdown-toggle.btn-success:hover,.btn-success:active:focus,.btn-success.active:focus,.open>.dropdown-toggle.btn-success:focus,.btn-success:active.focus,.btn-success.active.focus,.open>.dropdown-toggle.btn-success.focus{color:#ffffff;background-color:#2b6e44;border-color:#183e26}.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus{background-color:#43ac6a;border-color:#3c9a5f}.btn-success .badge{color:#43ac6a;background-color:#ffffff}.btn-info{color:#ffffff;background-color:#5bc0de;border-color:#46b8da}.btn-info:focus,.btn-info.focus{color:#ffffff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#ffffff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#ffffff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info:active:hover,.btn-info.active:hover,.open>.dropdown-toggle.btn-info:hover,.btn-info:active:focus,.btn-info.active:focus,.open>.dropdown-toggle.btn-info:focus,.btn-info:active.focus,.btn-info.active.focus,.open>.dropdown-toggle.btn-info.focus{color:#ffffff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#ffffff}.btn-warning{color:#ffffff;background-color:#e99002;border-color:#d08002}.btn-warning:focus,.btn-warning.focus{color:#ffffff;background-color:#b67102;border-color:#513201}.btn-warning:hover{color:#ffffff;background-color:#b67102;border-color:#935b01}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#ffffff;background-color:#b67102;background-image:none;border-color:#935b01}.btn-warning:active:hover,.btn-warning.active:hover,.open>.dropdown-toggle.btn-warning:hover,.btn-warning:active:focus,.btn-warning.active:focus,.open>.dropdown-toggle.btn-warning:focus,.btn-warning:active.focus,.btn-warning.active.focus,.open>.dropdown-toggle.btn-warning.focus{color:#ffffff;background-color:#935b01;border-color:#513201}.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus{background-color:#e99002;border-color:#d08002}.btn-warning .badge{color:#e99002;background-color:#ffffff}.btn-danger{color:#ffffff;background-color:#f04124;border-color:#ea2f10}.btn-danger:focus,.btn-danger.focus{color:#ffffff;background-color:#d32a0e;border-color:#731708}.btn-danger:hover{color:#ffffff;background-color:#d32a0e;border-color:#b1240c}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#ffffff;background-color:#d32a0e;background-image:none;border-color:#b1240c}.btn-danger:active:hover,.btn-danger.active:hover,.open>.dropdown-toggle.btn-danger:hover,.btn-danger:active:focus,.btn-danger.active:focus,.open>.dropdown-toggle.btn-danger:focus,.btn-danger:active.focus,.btn-danger.active.focus,.open>.dropdown-toggle.btn-danger.focus{color:#ffffff;background-color:#b1240c;border-color:#731708}.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus{background-color:#f04124;border-color:#ea2f10}.btn-danger .badge{color:#f04124;background-color:#ffffff}.btn-link{font-weight:400;color:#008cba;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#008cba;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}.btn-sm,.btn-group-sm>.btn{padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}.btn-xs,.btn-group-xs>.btn{padding:4px 6px;font-size:12px;line-height:1.5;border-radius:0}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.fade{opacity:0;transition:opacity 0.15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;transition-property:height, visibility;transition-duration:0.35s;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid \9;border-right:4px solid transparent;border-left:4px solid transparent}.dropup,.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:15px;text-align:left;list-style:none;background-color:#ffffff;background-clip:padding-box;border:1px solid #cccccc;border:1px solid rgba(0,0,0,0.15);border-radius:0;box-shadow:0 6px 12px rgba(0,0,0,0.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:rgba(0,0,0,0.2)}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.4;color:#555555;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{color:#262626;text-decoration:none;background-color:#eeeeee}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#ffffff;text-decoration:none;background-color:#008cba;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.4;color:#999999;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid \9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-group.open .dropdown-toggle.btn-link{box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle="buttons"]>.btn input[type="radio"],[data-toggle="buttons"]>.btn-group>.btn input[type="radio"],[data-toggle="buttons"]>.btn input[type="checkbox"],[data-toggle="buttons"]>.btn-group>.btn input[type="checkbox"]{position:absolute;clip:rect(0, 0, 0, 0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*="col-"]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:60px;padding:16px 20px;font-size:19px;line-height:1.3333333;border-radius:0}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:60px;line-height:60px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:36px;padding:8px 12px;font-size:12px;line-height:1.5;border-radius:0}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:36px;line-height:36px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:8px 12px;font-size:15px;font-weight:400;line-height:1;color:#6f6f6f;text-align:center;background-color:#eeeeee;border:1px solid #cccccc;border-radius:0}.input-group-addon.input-sm{padding:8px 12px;font-size:12px;border-radius:0}.input-group-addon.input-lg{padding:16px 20px;font-size:19px;border-radius:0}.input-group-addon input[type="radio"],.input-group-addon input[type="checkbox"]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eeeeee}.nav>li.disabled>a{color:#999999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999999;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eeeeee;border-color:#008cba}.nav .nav-divider{height:1px;margin:9.5px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #dddddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.4;border:1px solid transparent;border-radius:0 0 0 0}.nav-tabs>li>a:hover{border-color:#eeeeee #eeeeee #dddddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#6f6f6f;cursor:default;background-color:#ffffff;border:1px solid #dddddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#ffffff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:0}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#ffffff;background-color:#008cba}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #dddddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #dddddd;border-radius:0 0 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#ffffff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:45px;margin-bottom:21px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:0}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block !important;height:auto !important;padding-bottom:0;overflow:visible !important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:45px;padding:12px 15px;font-size:19px;line-height:21px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:5.5px;margin-bottom:5.5px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:0}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:6px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:21px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:21px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:12px;padding-bottom:12px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);margin-top:3px;margin-bottom:3px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn,.navbar-form .input-group .form-control{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .radio label,.navbar-form .checkbox label{padding-left:0}.navbar-form .radio input[type="radio"],.navbar-form .checkbox input[type="checkbox"]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:3px;margin-bottom:3px}.navbar-btn.btn-sm{margin-top:4.5px;margin-bottom:4.5px}.navbar-btn.btn-xs{margin-top:11.5px;margin-bottom:11.5px}.navbar-text{margin-top:12px;margin-bottom:12px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left !important}.navbar-right{float:right !important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#333333;border-color:#222222}.navbar-default .navbar-brand{color:#ffffff}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-default .navbar-text{color:#ffffff}.navbar-default .navbar-nav>li>a{color:#ffffff}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#cccccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{color:#ffffff;background-color:#272727}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#272727}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#cccccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:transparent}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:transparent}.navbar-default .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#222222}.navbar-default .navbar-link{color:#ffffff}.navbar-default .navbar-link:hover{color:#ffffff}.navbar-default .btn-link{color:#ffffff}.navbar-default .btn-link:hover,.navbar-default .btn-link:focus{color:#ffffff}.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:hover,.navbar-default .btn-link[disabled]:focus,fieldset[disabled] .navbar-default .btn-link:focus{color:#cccccc}.navbar-inverse{background-color:#008cba;border-color:#006687}.navbar-inverse .navbar-brand{color:#ffffff}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#ffffff;background-color:transparent}.navbar-inverse .navbar-text{color:#ffffff}.navbar-inverse .navbar-nav>li>a{color:#ffffff}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{color:#ffffff;background-color:#006687}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#ffffff}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#ffffff;background-color:#006687}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:transparent}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:transparent}.navbar-inverse .navbar-toggle .icon-bar{background-color:#ffffff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#007196}.navbar-inverse .navbar-link{color:#ffffff}.navbar-inverse .navbar-link:hover{color:#ffffff}.navbar-inverse .btn-link{color:#ffffff}.navbar-inverse .btn-link:hover,.navbar-inverse .btn-link:focus{color:#ffffff}.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:hover,.navbar-inverse .btn-link[disabled]:focus,fieldset[disabled] .navbar-inverse .btn-link:focus{color:#444444}.breadcrumb{padding:8px 15px;margin-bottom:21px;list-style:none;background-color:#f5f5f5;border-radius:0}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#999999;content:"/\00a0"}.breadcrumb>.active{color:#333333}.pagination{display:inline-block;padding-left:0;margin:21px 0;border-radius:0}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:8px 12px;margin-left:-1px;line-height:1.4;color:#008cba;text-decoration:none;background-color:transparent;border:1px solid transparent}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{z-index:2;color:#008cba;background-color:#eeeeee;border-color:transparent}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:0;border-bottom-left-radius:0}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:3;color:#ffffff;cursor:default;background-color:#008cba;border-color:transparent}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999999;cursor:not-allowed;background-color:#ffffff;border-color:transparent}.pagination-lg>li>a,.pagination-lg>li>span{padding:16px 20px;font-size:19px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pagination-sm>li>a,.pagination-sm>li>span{padding:8px 12px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:0;border-bottom-left-radius:0}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0}.pager{padding-left:0;margin:21px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:transparent;border:1px solid transparent;border-radius:3px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eeeeee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999999;cursor:not-allowed;background-color:transparent}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:hover,a.label:focus{color:#ffffff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999999}.label-default[href]:hover,.label-default[href]:focus{background-color:#808080}.label-primary{background-color:#008cba}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#006687}.label-success{background-color:#43ac6a}.label-success[href]:hover,.label-success[href]:focus{background-color:#358753}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#e99002}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#b67102}.label-danger{background-color:#f04124}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#d32a0e}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:bold;line-height:1;color:#ffffff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#008cba;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge,.btn-group-xs>.btn .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#ffffff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#008cba;background-color:#ffffff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#fafafa}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:23px;font-weight:200}.jumbotron>hr{border-top-color:#e1e1e1}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:0}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron h1,.jumbotron .h1{font-size:68px}}.thumbnail{display:block;padding:4px;margin-bottom:21px;line-height:1.4;background-color:#ffffff;border:1px solid #dddddd;border-radius:0;transition:border .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-right:auto;margin-left:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#008cba}.thumbnail .caption{padding:9px;color:#222222}.alert{padding:15px;margin-bottom:21px;border:1px solid transparent;border-radius:0}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:bold}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#ffffff;background-color:#43ac6a;border-color:#3c9a5f}.alert-success hr{border-top-color:#358753}.alert-success .alert-link{color:#e6e6e6}.alert-info{color:#ffffff;background-color:#5bc0de;border-color:#3db5d8}.alert-info hr{border-top-color:#2aabd2}.alert-info .alert-link{color:#e6e6e6}.alert-warning{color:#ffffff;background-color:#e99002;border-color:#d08002}.alert-warning hr{border-top-color:#b67102}.alert-warning .alert-link{color:#e6e6e6}.alert-danger{color:#ffffff;background-color:#f04124;border-color:#ea2f10}.alert-danger hr{border-top-color:#d32a0e}.alert-danger .alert-link{color:#e6e6e6}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:21px;margin-bottom:21px;overflow:hidden;background-color:#f5f5f5;border-radius:0;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:21px;color:#ffffff;text-align:center;background-color:#008cba;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);transition:width 0.6s ease}.progress-striped .progress-bar,.progress-bar-striped{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-size:40px 40px}.progress.active .progress-bar,.progress-bar.active{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#43ac6a}.progress-striped .progress-bar-success{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-warning{background-color:#e99002}.progress-striped .progress-bar-warning{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.progress-bar-danger{background-color:#f04124}.progress-striped .progress-bar-danger{background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-left,.media-right,.media-body{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#ffffff;border:1px solid #dddddd}.list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.list-group-item.disabled,.list-group-item.disabled:hover,.list-group-item.disabled:focus{color:#999999;cursor:not-allowed;background-color:#eeeeee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text{color:#999999}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{z-index:2;color:#ffffff;background-color:#008cba;border-color:#008cba}.list-group-item.active .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>.small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:hover .list-group-item-text,.list-group-item.active:focus .list-group-item-text{color:#87e1ff}a.list-group-item,button.list-group-item{color:#555555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333333}a.list-group-item:hover,button.list-group-item:hover,a.list-group-item:focus,button.list-group-item:focus{color:#555555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#43ac6a;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#43ac6a}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,button.list-group-item-success:hover,a.list-group-item-success:focus,button.list-group-item-success:focus{color:#43ac6a;background-color:#d0e9c6}a.list-group-item-success.active,button.list-group-item-success.active,a.list-group-item-success.active:hover,button.list-group-item-success.active:hover,a.list-group-item-success.active:focus,button.list-group-item-success.active:focus{color:#fff;background-color:#43ac6a;border-color:#43ac6a}.list-group-item-info{color:#5bc0de;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#5bc0de}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,button.list-group-item-info:hover,a.list-group-item-info:focus,button.list-group-item-info:focus{color:#5bc0de;background-color:#c4e3f3}a.list-group-item-info.active,button.list-group-item-info.active,a.list-group-item-info.active:hover,button.list-group-item-info.active:hover,a.list-group-item-info.active:focus,button.list-group-item-info.active:focus{color:#fff;background-color:#5bc0de;border-color:#5bc0de}.list-group-item-warning{color:#e99002;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#e99002}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,button.list-group-item-warning:hover,a.list-group-item-warning:focus,button.list-group-item-warning:focus{color:#e99002;background-color:#faf2cc}a.list-group-item-warning.active,button.list-group-item-warning.active,a.list-group-item-warning.active:hover,button.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus,button.list-group-item-warning.active:focus{color:#fff;background-color:#e99002;border-color:#e99002}.list-group-item-danger{color:#f04124;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#f04124}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,button.list-group-item-danger:hover,a.list-group-item-danger:focus,button.list-group-item-danger:focus{color:#f04124;background-color:#ebcccc}a.list-group-item-danger.active,button.list-group-item-danger.active,a.list-group-item-danger.active:hover,button.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus,button.list-group-item-danger.active:focus{color:#fff;background-color:#f04124;border-color:#f04124}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:21px;background-color:#ffffff;border:1px solid transparent;border-radius:0;box-shadow:0 1px 1px rgba(0,0,0,0.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:-1;border-top-right-radius:-1}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:17px;color:inherit}.panel-title>a,.panel-title>small,.panel-title>.small,.panel-title>small>a,.panel-title>.small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #dddddd;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:-1;border-top-right-radius:-1}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.table,.panel>.table-responsive>.table,.panel>.panel-collapse>.table{margin-bottom:0}.panel>.table caption,.panel>.table-responsive>.table caption,.panel>.panel-collapse>.table caption{padding-right:15px;padding-left:15px}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child{border-top-left-radius:-1;border-top-right-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:-1}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:-1}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:-1;border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:-1}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:-1}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #dddddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:21px}.panel-group .panel{margin-bottom:0;border-radius:0}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.panel-body,.panel-group .panel-heading+.panel-collapse>.list-group{border-top:1px solid #dddddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #dddddd}.panel-default{border-color:#dddddd}.panel-default>.panel-heading{color:#333333;background-color:#f5f5f5;border-color:#dddddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#dddddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#dddddd}.panel-primary{border-color:#008cba}.panel-primary>.panel-heading{color:#ffffff;background-color:#008cba;border-color:#008cba}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#008cba}.panel-primary>.panel-heading .badge{color:#008cba;background-color:#ffffff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#008cba}.panel-success{border-color:#3c9a5f}.panel-success>.panel-heading{color:#ffffff;background-color:#43ac6a;border-color:#3c9a5f}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3c9a5f}.panel-success>.panel-heading .badge{color:#43ac6a;background-color:#ffffff}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3c9a5f}.panel-info{border-color:#3db5d8}.panel-info>.panel-heading{color:#ffffff;background-color:#5bc0de;border-color:#3db5d8}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#3db5d8}.panel-info>.panel-heading .badge{color:#5bc0de;background-color:#ffffff}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#3db5d8}.panel-warning{border-color:#d08002}.panel-warning>.panel-heading{color:#ffffff;background-color:#e99002;border-color:#d08002}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d08002}.panel-warning>.panel-heading .badge{color:#e99002;background-color:#ffffff}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d08002}.panel-danger{border-color:#ea2f10}.panel-danger>.panel-heading{color:#ffffff;background-color:#f04124;border-color:#ea2f10}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ea2f10}.panel-danger>.panel-heading .badge{color:#f04124;background-color:#ffffff}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ea2f10}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive iframe,.embed-responsive embed,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#fafafa;border:1px solid #e8e8e8;border-radius:0;box-shadow:inset 0 1px 1px rgba(0,0,0,0.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,0.15)}.well-lg{padding:24px;border-radius:0}.well-sm{padding:9px;border-radius:0}.close{float:right;font-size:22.5px;font-weight:bold;line-height:1;color:#ffffff;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=20);opacity:0.2}.close:hover,.close:focus{color:#ffffff;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:0.5}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0, -25%);transform:translate(0, -25%);transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0, 0);transform:translate(0, 0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#ffffff;background-clip:padding-box;border:1px solid #999999;border:1px solid rgba(0,0,0,0.2);border-radius:0;box-shadow:0 3px 9px rgba(0,0,0,0.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:0.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.4}.modal-body{position:relative;padding:20px}.modal-footer{padding:20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{box-shadow:0 5px 15px rgba(0,0,0,0.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.4;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:0.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#333333}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#333333}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#333333}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#333333}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#333333}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#333333}.tooltip-inner{max-width:200px;padding:3px 8px;color:#ffffff;text-align:center;background-color:#333333;border-radius:0}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.4;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:15px;background-color:#333333;background-clip:padding-box;border:1px solid #333333;border:1px solid transparent;border-radius:0;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#000000;border-top-color:rgba(0,0,0,0.05);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#333333;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#000000;border-right-color:rgba(0,0,0,0.05);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#333333;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#000000;border-bottom-color:rgba(0,0,0,0.05)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#333333}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#000000;border-left-color:rgba(0,0,0,0.05)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#333333}.popover-title{padding:8px 14px;margin:0;font-size:15px;background-color:#333333;border-bottom:1px solid #262626;border-radius:-1 -1 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.next,.carousel-inner>.item.active.right{-webkit-transform:translate3d(100%, 0, 0);transform:translate3d(100%, 0, 0);left:0}.carousel-inner>.item.prev,.carousel-inner>.item.active.left{-webkit-transform:translate3d(-100%, 0, 0);transform:translate3d(-100%, 0, 0);left:0}.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right,.carousel-inner>.item.active{-webkit-transform:translate3d(0, 0, 0);transform:translate3d(0, 0, 0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:0.5}.carousel-control.left{background-image:linear-gradient(to right, rgba(0,0,0,0.5) 0, rgba(0,0,0,0.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:linear-gradient(to right, rgba(0,0,0,0.0001) 0, rgba(0,0,0,0.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:hover,.carousel-control:focus{color:#ffffff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:0.9}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%;margin-left:-10px}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%;margin-right:-10px}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #ffffff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#ffffff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#ffffff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,0.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-header:before,.modal-header:after,.modal-footer:before,.modal-footer:after{display:table;content:" "}.clearfix:after,.dl-horizontal dd:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-header:after,.modal-footer:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none !important}.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block{display:none !important}@media (max-width:767px){.visible-xs{display:block !important}table.visible-xs{display:table !important}tr.visible-xs{display:table-row !important}th.visible-xs,td.visible-xs{display:table-cell !important}}@media (max-width:767px){.visible-xs-block{display:block !important}}@media (max-width:767px){.visible-xs-inline{display:inline !important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block !important}table.visible-sm{display:table !important}tr.visible-sm{display:table-row !important}th.visible-sm,td.visible-sm{display:table-cell !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline !important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block !important}table.visible-md{display:table !important}tr.visible-md{display:table-row !important}th.visible-md,td.visible-md{display:table-cell !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline !important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block !important}}@media (min-width:1200px){.visible-lg{display:block !important}table.visible-lg{display:table !important}tr.visible-lg{display:table-row !important}th.visible-lg,td.visible-lg{display:table-cell !important}}@media (min-width:1200px){.visible-lg-block{display:block !important}}@media (min-width:1200px){.visible-lg-inline{display:inline !important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block !important}}@media (max-width:767px){.hidden-xs{display:none !important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none !important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none !important}}@media (min-width:1200px){.hidden-lg{display:none !important}}.visible-print{display:none !important}@media print{.visible-print{display:block !important}table.visible-print{display:table !important}tr.visible-print{display:table-row !important}th.visible-print,td.visible-print{display:table-cell !important}}.visible-print-block{display:none !important}@media print{.visible-print-block{display:block !important}}.visible-print-inline{display:none !important}@media print{.visible-print-inline{display:inline !important}}.visible-print-inline-block{display:none !important}@media print{.visible-print-inline-block{display:inline-block !important}}@media print{.hidden-print{display:none !important}}.navbar{border:none;font-size:13px;font-weight:300}.navbar .navbar-toggle:hover .icon-bar{background-color:#b3b3b3}.navbar-collapse{border-top-color:rgba(0,0,0,0.2);box-shadow:none}.navbar .btn{padding-top:6px;padding-bottom:6px}.navbar-form{margin-top:7px;margin-bottom:5px}.navbar-form .form-control{height:auto;padding:4px 6px}.navbar-text{margin:12px 15px;line-height:21px}.navbar .dropdown-menu{border:none}.navbar .dropdown-menu>li>a,.navbar .dropdown-menu>li>a:focus{background-color:transparent;font-size:13px;font-weight:300}.navbar .dropdown-header{color:rgba(255,255,255,0.5)}.navbar-default .dropdown-menu{background-color:#333333}.navbar-default .dropdown-menu>li>a,.navbar-default .dropdown-menu>li>a:focus{color:#ffffff}.navbar-default .dropdown-menu>li>a:hover,.navbar-default .dropdown-menu>.active>a,.navbar-default .dropdown-menu>.active>a:hover{background-color:#272727}.navbar-inverse .dropdown-menu{background-color:#008cba}.navbar-inverse .dropdown-menu>li>a,.navbar-inverse .dropdown-menu>li>a:focus{color:#ffffff}.navbar-inverse .dropdown-menu>li>a:hover,.navbar-inverse .dropdown-menu>.active>a,.navbar-inverse .dropdown-menu>.active>a:hover{background-color:#006687}.btn{padding:8px 12px}.btn-lg{padding:16px 20px}.btn-sm{padding:8px 12px}.btn-xs{padding:4px 6px}.btn-group .btn~.dropdown-toggle{padding-left:16px;padding-right:16px}.btn-group .dropdown-menu{border-top-width:0}.btn-group.dropup .dropdown-menu{border-top-width:1px;border-bottom-width:0;margin-bottom:0}.btn-group .dropdown-toggle.btn-default~.dropdown-menu{background-color:#e7e7e7;border-color:#cccccc}.btn-group .dropdown-toggle.btn-default~.dropdown-menu>li>a{color:#333333}.btn-group .dropdown-toggle.btn-default~.dropdown-menu>li>a:hover{background-color:#d3d3d3}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu{background-color:#008cba;border-color:#0079a1}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-primary~.dropdown-menu>li>a:hover{background-color:#006d91}.btn-group .dropdown-toggle.btn-success~.dropdown-menu{background-color:#43ac6a;border-color:#3c9a5f}.btn-group .dropdown-toggle.btn-success~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-success~.dropdown-menu>li>a:hover{background-color:#388f58}.btn-group .dropdown-toggle.btn-info~.dropdown-menu{background-color:#5bc0de;border-color:#46b8da}.btn-group .dropdown-toggle.btn-info~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-info~.dropdown-menu>li>a:hover{background-color:#39b3d7}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu{background-color:#e99002;border-color:#d08002}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-warning~.dropdown-menu>li>a:hover{background-color:#c17702}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu{background-color:#f04124;border-color:#ea2f10}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu>li>a{color:#ffffff}.btn-group .dropdown-toggle.btn-danger~.dropdown-menu>li>a:hover{background-color:#dc2c0f}.lead{color:#6f6f6f}cite{font-style:italic}blockquote{border-left-width:1px;color:#6f6f6f}blockquote.pull-right{border-right-width:1px}blockquote small{font-size:12px;font-weight:300}table{font-size:12px}label,.control-label,.help-block,.checkbox,.radio{font-size:12px;font-weight:normal}input[type="radio"],input[type="checkbox"]{margin-top:1px}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{border-color:transparent}.nav-tabs>li>a{background-color:#e7e7e7;color:#222222}.nav-tabs .caret{border-top-color:#222222;border-bottom-color:#222222}.nav-pills{font-weight:300}.breadcrumb{border:1px solid #dddddd;border-radius:3px;font-size:10px;font-weight:300;text-transform:uppercase}.pagination{font-size:12px;font-weight:300;color:#999999}.pagination>li>a,.pagination>li>span{margin-left:4px;color:#999999}.pagination>.active>a,.pagination>.active>span{color:#fff}.pagination>li>a,.pagination>li:first-child>a,.pagination>li:last-child>a,.pagination>li>span,.pagination>li:first-child>span,.pagination>li:last-child>span{border-radius:3px}.pagination-lg>li>a,.pagination-lg>li>span{padding-left:22px;padding-right:22px}.pagination-sm>li>a,.pagination-sm>li>span{padding:0 5px}.pager{font-size:12px;font-weight:300;color:#999999}.list-group{font-size:12px;font-weight:300}.close{opacity:0.4;text-decoration:none;text-shadow:none}.close:hover,.close:focus{opacity:1}.alert{font-size:12px;font-weight:300}.alert .alert-link{font-weight:normal;color:#fff;text-decoration:underline}.label{padding-left:1em;padding-right:1em;border-radius:0;font-weight:300}.label-default{background-color:#e7e7e7;color:#333333}.badge{font-weight:300}.progress{height:22px;padding:2px;background-color:#f6f6f6;border:1px solid #ccc;box-shadow:none}.dropdown-menu{padding:0;margin-top:0;font-size:12px}.dropdown-menu>li>a{padding:12px 15px}.dropdown-header{padding-left:15px;padding-right:15px;font-size:9px;text-transform:uppercase}.popover{color:#fff;font-size:12px;font-weight:300}.panel-heading,.panel-footer{border-top-right-radius:0;border-top-left-radius:0}.panel-default .close{color:#222222}.modal .close{color:#222222} diff --git a/users/forms.py b/users/forms.py index 570abcb0..d1cdbc4c 100644 --- a/users/forms.py +++ b/users/forms.py @@ -41,11 +41,14 @@ of each of the method. from __future__ import unicode_literals +from os import walk, path + from django import forms from django.forms import ModelForm, Form from django.contrib.auth.forms import ReadOnlyPasswordHashField from django.contrib.auth.password_validation import validate_password, password_validators_help_text_html from django.core.validators import MinLengthValidator +from django.conf import settings from django.utils import timezone from django.utils.functional import lazy from django.contrib.auth.models import Group, Permission @@ -1040,3 +1043,17 @@ class InitialRegisterForm(forms.Form): if self.cleaned_data["register_machine"]: if self.mac_address and self.nas_type: self.user.autoregister_machine(self.mac_address, self.nas_type) + + +class ThemeForm(FormRevMixin, forms.Form): + """Form to change the theme of a user. + """ + + theme = forms.ChoiceField(widget=forms.Select()) + + def __init__(self, *args, **kwargs): + _, _ ,themes = next(walk(path.join(settings.STATIC_ROOT, "css/themes"))) + if not themes: + themes = ["default.css"] + super(ThemeForm, self).__init__(*args, **kwargs) + self.fields['theme'].choices = [(theme, theme) for theme in themes] \ No newline at end of file diff --git a/users/migrations/0095_user_theme.py b/users/migrations/0095_user_theme.py new file mode 100644 index 00000000..162903ef --- /dev/null +++ b/users/migrations/0095_user_theme.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-11-16 18:52 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0094_remove_user_profile_image'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='theme', + field=models.CharField(default='default.css', max_length=255), + ), + ] diff --git a/users/models.py b/users/models.py index e6bb65c1..fac7c2a6 100755 --- a/users/models.py +++ b/users/models.py @@ -305,6 +305,7 @@ class User( verbose_name=_("enable shortcuts on Re2o website"), default=True ) email_change_date = models.DateTimeField(auto_now_add=True) + theme = models.CharField(max_length=255, default="default.css") USERNAME_FIELD = "pseudo" REQUIRED_FIELDS = ["surname", "email"] @@ -1963,6 +1964,14 @@ class User( def __str__(self): return self.pseudo + @property + def theme_name(self): + """Return the theme without the extension + + Returns: + str: name of theme + """ + return self.theme.split(".")[0] class Adherent(User): """Base re2o Adherent model, inherit from User. Add other attributes. diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index a676b416..fddf9af4 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -176,6 +176,10 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Edit the groups" %} {% acl_end %} + + + {% trans "Change theme" %} + {% history_button users text=True %} @@ -345,6 +349,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% trans "Shortcuts enabled" %}
    {{ users.shortcuts_enabled | tick }}
    +
    +
    {% trans "Theme" %}
    +
    {{ request.user.theme_name }}
    +
    diff --git a/users/urls.py b/users/urls.py index 19a39adf..91f3f3a9 100644 --- a/users/urls.py +++ b/users/urls.py @@ -127,4 +127,5 @@ urlpatterns = [ url(r"^$", views.index, name="index"), url(r"^index_clubs/$", views.index_clubs, name="index-clubs"), url(r"^initial_register/$", views.initial_register, name="initial-register"), + url(r"^edit_theme/(?P[0-9]+)$", views.edit_theme, name="edit-theme"), ] diff --git a/users/views.py b/users/views.py index e0478783..c950b197 100644 --- a/users/views.py +++ b/users/views.py @@ -128,6 +128,7 @@ from .forms import ( ClubAdminandMembersForm, GroupForm, InitialRegisterForm, + ThemeForm ) import os @@ -1593,3 +1594,27 @@ def initial_register(request): request, ) +@login_required +@can_edit(User) +def edit_theme(request, user, userid): + """View for editing base user informations. + Perform an acl check on user instance. + + Parameters: + request (django request): Standard django request. + user: User instance to edit + + Returns: + Django User form. + + """ + theme_form = ThemeForm(request.POST or None, initial={'theme':user.theme}) + if theme_form.is_valid(): + user.theme = theme_form.cleaned_data["theme"] + user.save() + messages.success(request, _("The theme was edited.")) + + return redirect(reverse("users:profil", kwargs={"userid": str(userid)})) + return form( + {"userform": theme_form, "action_name": _("Edit")}, "users/user.html", request, + ) \ No newline at end of file From 0202a777990a3d06a1500011fcd3d586fb75c408 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Tue, 17 Nov 2020 21:50:16 +0100 Subject: [PATCH 380/490] Translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 379 +++++------ logs/locale/fr/LC_MESSAGES/django.po | 140 ++-- machines/locale/fr/LC_MESSAGES/django.po | 515 +++++++-------- multi_op/locale/fr/LC_MESSAGES/django.po | 85 +-- preferences/locale/fr/LC_MESSAGES/django.po | 658 ++++++++++--------- re2o/locale/fr/LC_MESSAGES/django.po | 80 ++- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 479 +++++++++----- tickets/locale/fr/LC_MESSAGES/django.po | 76 +-- topologie/locale/fr/LC_MESSAGES/django.po | 507 +++++++-------- users/locale/fr/LC_MESSAGES/django.po | 675 +++++++++----------- 12 files changed, 1842 insertions(+), 1756 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index d06f3230..797b102a 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index ca251fb5..b31bf830 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -37,7 +37,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:688 +#: cotisations/forms.py:73 cotisations/models.py:695 msgid "Member" msgstr "Adhérent" @@ -130,48 +130,48 @@ msgstr "" msgid "date" msgstr "date" -#: cotisations/models.py:130 +#: cotisations/models.py:140 msgid "cheque number" msgstr "numéro de chèque" -#: cotisations/models.py:133 +#: cotisations/models.py:143 msgid "validated" msgstr "validé" -#: cotisations/models.py:135 +#: cotisations/models.py:145 msgid "controlled" msgstr "contrôlé" -#: cotisations/models.py:141 +#: cotisations/models.py:151 msgid "Can edit the \"controlled\" state" msgstr "Peut modifier l'état \"contrôlé\"" -#: cotisations/models.py:142 +#: cotisations/models.py:152 msgid "Can view an invoice object" msgstr "Peut voir un objet facture" -#: cotisations/models.py:143 +#: cotisations/models.py:153 msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:145 cotisations/models.py:462 cotisations/views.py:376 -#: cotisations/views.py:571 +#: cotisations/models.py:155 cotisations/models.py:451 cotisations/views.py:380 +#: cotisations/views.py:575 msgid "invoice" msgstr "facture" -#: cotisations/models.py:146 +#: cotisations/models.py:156 msgid "invoices" msgstr "factures" -#: cotisations/models.py:160 +#: cotisations/models.py:170 msgid "You don't have the right to edit an invoice." msgstr "Vous n'avez pas le droit de modifier une facture." -#: cotisations/models.py:168 +#: cotisations/models.py:178 msgid "You don't have the right to edit this user's invoices." msgstr "Vous n'avez pas le droit de modifier les factures de cet utilisateur." -#: cotisations/models.py:177 +#: cotisations/models.py:187 msgid "" "You don't have the right to edit an invoice already controlled or " "invalidated." @@ -179,15 +179,15 @@ msgstr "" "Vous n'avez pas le droit de modifier une facture précédemment contrôlée ou " "invalidée." -#: cotisations/models.py:192 +#: cotisations/models.py:202 msgid "You don't have the right to delete an invoice." msgstr "Vous n'avez pas le droit de supprimer une facture." -#: cotisations/models.py:200 +#: cotisations/models.py:210 msgid "You don't have the right to delete this user's invoices." msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur." -#: cotisations/models.py:209 +#: cotisations/models.py:219 msgid "" "You don't have the right to delete an invoice already controlled or " "invalidated." @@ -195,137 +195,123 @@ msgstr "" "Vous n'avez pas le droit de supprimer une facture précédemment contrôlée ou " "invalidée." -#: cotisations/models.py:223 +#: cotisations/models.py:233 msgid "You don't have the right to view someone else's invoices history." msgstr "" "Vous n'avez pas le droit de voir l'historique des factures d'un autre " "utilisateur." -#: cotisations/models.py:231 +#: cotisations/models.py:241 msgid "The invoice has been invalidated." msgstr "La facture a été invalidée." -#: cotisations/models.py:246 +#: cotisations/models.py:256 msgid "You don't have the right to edit the \"controlled\" state." msgstr "Vous n'avez pas le droit de modifier le statut \"contrôlé\"." -#: cotisations/models.py:265 +#: cotisations/models.py:275 msgid "There are no payment methods that you can use." msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." -#: cotisations/models.py:271 +#: cotisations/models.py:281 msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:378 +#: cotisations/models.py:374 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:380 +#: cotisations/models.py:376 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:381 +#: cotisations/models.py:377 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:382 +#: cotisations/models.py:378 msgid "address" msgstr "adresse" -#: cotisations/models.py:383 +#: cotisations/models.py:379 msgid "paid" msgstr "payé" -#: cotisations/models.py:384 +#: cotisations/models.py:380 msgid "remark" msgstr "remarque" -#: cotisations/models.py:389 +#: cotisations/models.py:385 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:392 +#: cotisations/models.py:388 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:427 +#: cotisations/models.py:423 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:433 +#: cotisations/models.py:429 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:455 cotisations/models.py:694 -#: cotisations/models.py:952 -msgid "Connection" -msgstr "Connexion" - -#: cotisations/models.py:456 cotisations/models.py:695 -#: cotisations/models.py:953 -msgid "Membership" -msgstr "Adhésion" - -#: cotisations/models.py:457 cotisations/models.py:690 -#: cotisations/models.py:696 cotisations/models.py:954 -msgid "Both of them" -msgstr "Les deux" - -#: cotisations/models.py:466 +#: cotisations/models.py:455 msgid "amount" msgstr "montant" -#: cotisations/models.py:469 +#: cotisations/models.py:460 msgid "article" msgstr "article" -#: cotisations/models.py:472 +#: cotisations/models.py:463 msgid "price" msgstr "prix" -#: cotisations/models.py:475 cotisations/models.py:708 -msgid "duration (in months)" -msgstr "durée (en mois)" +#: cotisations/models.py:466 cotisations/models.py:716 +msgid "duration of the connection (in months)" +msgstr "durée de la connexion (en mois)" -#: cotisations/models.py:481 cotisations/models.py:714 -msgid "duration (in days, will be added to duration in months)" -msgstr "durée (en jours, sera ajoutée à la durée en mois)" +#: cotisations/models.py:471 cotisations/models.py:720 +msgid "" +"duration of the connection (in days, will be added to duration in months)" +msgstr "durée de la connexion (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:489 cotisations/models.py:728 -#: cotisations/models.py:965 -msgid "subscription type" -msgstr "type de cotisation" +#: cotisations/models.py:474 cotisations/models.py:708 +msgid "duration of the membership (in months)" +msgstr "durée de l'adhésion (en mois)" -#: cotisations/models.py:494 +#: cotisations/models.py:479 cotisations/models.py:712 +msgid "" +"duration of the membership (in days, will be added to duration in months)" +msgstr "durée de l'adhésion (en jours, sera ajoutée à la durée en mois)" + +#: cotisations/models.py:484 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:495 +#: cotisations/models.py:485 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:497 cotisations/models.py:959 +#: cotisations/models.py:487 cotisations/models.py:957 msgid "purchase" msgstr "achat" -#: cotisations/models.py:498 +#: cotisations/models.py:488 msgid "purchases" msgstr "achats" -#: cotisations/models.py:551 cotisations/models.py:748 -msgid "Duration must be specified for a subscription." -msgstr "La durée doit être renseignée pour une cotisation." - -#: cotisations/models.py:562 +#: cotisations/models.py:563 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:568 +#: cotisations/models.py:569 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:577 +#: cotisations/models.py:578 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -333,15 +319,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:592 +#: cotisations/models.py:593 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:598 +#: cotisations/models.py:599 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:605 +#: cotisations/models.py:606 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -349,134 +335,150 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:621 +#: cotisations/models.py:622 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:689 +#: cotisations/models.py:696 msgid "Club" msgstr "Club" -#: cotisations/models.py:699 +#: cotisations/models.py:697 +msgid "Both of them" +msgstr "Les deux" + +#: cotisations/models.py:700 msgid "designation" msgstr "désignation" -#: cotisations/models.py:702 +#: cotisations/models.py:703 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:720 +#: cotisations/models.py:725 +msgid "need membership to be purchased" +msgstr "nécessite une adhésion pour être acheté" + +#: cotisations/models.py:732 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:731 cotisations/models.py:832 +#: cotisations/models.py:735 cotisations/models.py:836 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:738 +#: cotisations/models.py:742 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:739 +#: cotisations/models.py:743 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:746 +#: cotisations/models.py:750 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:771 +#: cotisations/models.py:773 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:812 +#: cotisations/models.py:816 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:813 +#: cotisations/models.py:817 msgid "bank" msgstr "banque" -#: cotisations/models.py:814 +#: cotisations/models.py:818 msgid "banks" msgstr "banques" -#: cotisations/models.py:830 +#: cotisations/models.py:834 msgid "method" msgstr "moyen" -#: cotisations/models.py:837 +#: cotisations/models.py:841 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:838 +#: cotisations/models.py:842 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:844 +#: cotisations/models.py:848 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:845 +#: cotisations/models.py:849 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:847 +#: cotisations/models.py:851 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:848 +#: cotisations/models.py:852 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:887 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:891 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:897 +#: cotisations/models.py:901 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:917 +#: cotisations/models.py:921 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:936 +#: cotisations/models.py:940 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:967 -msgid "start date" -msgstr "date de début" +#: cotisations/models.py:959 +msgid "start date for the connection" +msgstr "date de début pour la connexion" -#: cotisations/models.py:968 -msgid "end date" -msgstr "date de fin" +#: cotisations/models.py:960 +msgid "end date for the connection" +msgstr "date de fin pour la connexion" -#: cotisations/models.py:972 +#: cotisations/models.py:961 +msgid "start date for the membership" +msgstr "date de début pour l'adhésion" + +#: cotisations/models.py:962 +msgid "end date for the membership" +msgstr "date de fin pour l'adhésion" + +#: cotisations/models.py:966 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:973 +#: cotisations/models.py:967 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:975 +#: cotisations/models.py:969 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:976 +#: cotisations/models.py:970 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:982 +#: cotisations/models.py:976 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:991 +#: cotisations/models.py:985 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -484,11 +486,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1003 +#: cotisations/models.py:997 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:1010 +#: cotisations/models.py:1004 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -496,7 +498,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1026 +#: cotisations/models.py:1020 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -646,22 +648,26 @@ msgid "Price" msgstr "Prix" #: cotisations/templates/cotisations/aff_article.html:35 -msgid "Subscription type" -msgstr "Type de cotisation" +msgid "Duration membership (in months)" +msgstr "Durée adhésion (en mois)" #: cotisations/templates/cotisations/aff_article.html:36 -msgid "Duration (in months)" -msgstr "Durée (en mois)" +msgid "Duration membership (in days)" +msgstr "Durée adhésion (en jours)" #: cotisations/templates/cotisations/aff_article.html:37 -msgid "Duration (in days)" -msgstr "Durée (en jours)" +msgid "Duration connection (in months)" +msgstr "Durée connexion (en mois)" #: cotisations/templates/cotisations/aff_article.html:38 +msgid "Duration connection (in days)" +msgstr "Durée connexion (en jours)" + +#: cotisations/templates/cotisations/aff_article.html:39 msgid "Concerned users" msgstr "Utilisateurs concernés" -#: cotisations/templates/cotisations/aff_article.html:39 +#: cotisations/templates/cotisations/aff_article.html:40 #: cotisations/templates/cotisations/aff_paiement.html:34 msgid "Available for everyone" msgstr "Disponible pour tous" @@ -710,7 +716,7 @@ msgid "Invoice created" msgstr "Facture créée" #: cotisations/templates/cotisations/aff_cost_estimate.html:91 -#: cotisations/templates/cotisations/aff_cotisations.html:81 +#: cotisations/templates/cotisations/aff_cotisations.html:91 #: cotisations/templates/cotisations/aff_custom_invoice.html:79 msgid "PDF" msgstr "PDF" @@ -725,15 +731,15 @@ msgstr "Utilisateur" msgid "Invoice ID" msgstr "ID facture" -#: cotisations/templates/cotisations/aff_cotisations.html:71 +#: cotisations/templates/cotisations/aff_cotisations.html:81 msgid "Controlled invoice" msgstr "Facture contrôlée" -#: cotisations/templates/cotisations/aff_cotisations.html:84 +#: cotisations/templates/cotisations/aff_cotisations.html:94 msgid "Invalidated invoice" msgstr "Facture invalidée" -#: cotisations/templates/cotisations/aff_cotisations.html:88 +#: cotisations/templates/cotisations/aff_cotisations.html:98 msgid "Voucher" msgstr "Reçu" @@ -749,6 +755,23 @@ msgstr "Type de paiement" msgid "Custom payment method" msgstr "Moyen de paiement personnalisé" +#: cotisations/templates/cotisations/aff_profil.html:35 +#: cotisations/templates/cotisations/index.html:32 +msgid "Subscriptions" +msgstr "Cotisations" + +#: cotisations/templates/cotisations/aff_profil.html:43 +msgid "Add a subscription" +msgstr "Ajouter une cotisation" + +#: cotisations/templates/cotisations/aff_profil.html:48 +msgid "Edit the balance" +msgstr "Modifier le solde utilisateur" + +#: cotisations/templates/cotisations/aff_profil.html:57 +msgid "No invoice" +msgstr "Pas de facture" + #: cotisations/templates/cotisations/control.html:30 msgid "Invoice control" msgstr "Contrôle des factures" @@ -784,7 +807,7 @@ msgstr "Contrôlé" #: cotisations/templates/cotisations/control.html:107 #: cotisations/templates/cotisations/delete.html:38 #: cotisations/templates/cotisations/edit_facture.html:64 -#: cotisations/views.py:170 cotisations/views.py:222 cotisations/views.py:276 +#: cotisations/views.py:172 cotisations/views.py:224 cotisations/views.py:280 msgid "Confirm" msgstr "Confirmer" @@ -813,7 +836,6 @@ msgstr "Modifier la facture" #: cotisations/templates/cotisations/edit_facture.html:45 #: cotisations/templates/cotisations/facture.html:59 #: cotisations/templates/cotisations/index_article.html:30 -#: cotisations/templates/cotisations/sidebar.html:55 msgid "Articles" msgstr "Articles" @@ -840,14 +862,9 @@ msgid "Total price: 0,00 €" msgstr "Prix total : 0,00 €" #: cotisations/templates/cotisations/index.html:29 -#: cotisations/templates/cotisations/sidebar.html:40 msgid "Invoices" msgstr "Factures" -#: cotisations/templates/cotisations/index.html:32 -msgid "Subscriptions" -msgstr "Cotisations" - #: cotisations/templates/cotisations/index_article.html:33 msgid "List of articles" msgstr "Liste des articles" @@ -861,7 +878,6 @@ msgid "Delete one or several articles" msgstr "Supprimer un ou plusieurs articles" #: cotisations/templates/cotisations/index_banque.html:30 -#: cotisations/templates/cotisations/sidebar.html:60 msgid "Banks" msgstr "Banques" @@ -878,7 +894,6 @@ msgid "Delete one or several banks" msgstr "Supprimer une ou plusieurs banques" #: cotisations/templates/cotisations/index_cost_estimate.html:28 -#: cotisations/templates/cotisations/sidebar.html:50 msgid "Cost estimates" msgstr "Devis" @@ -887,7 +902,6 @@ msgid "List of cost estimates" msgstr "Liste des devis" #: cotisations/templates/cotisations/index_custom_invoice.html:28 -#: cotisations/templates/cotisations/sidebar.html:45 msgid "Custom invoices" msgstr "Factures personnalisées" @@ -896,7 +910,6 @@ msgid "List of custom invoices" msgstr "Liste des factures personnalisées" #: cotisations/templates/cotisations/index_paiement.html:30 -#: cotisations/templates/cotisations/sidebar.html:65 msgid "Payment methods" msgstr "Moyens de paiement" @@ -921,132 +934,124 @@ msgstr "Rechargement de solde" msgid "Pay %(amount)s €" msgstr "Payer %(amount)s €" -#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1026 +#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1030 msgid "Pay" msgstr "Payer" -#: cotisations/templates/cotisations/sidebar.html:32 -msgid "Create an invoice" -msgstr "Créer une facture" - -#: cotisations/templates/cotisations/sidebar.html:35 -msgid "Control the invoices" -msgstr "Contrôler les factures" - -#: cotisations/views.py:157 +#: cotisations/views.py:159 msgid "You need to choose at least one article." msgstr "Vous devez choisir au moins un article." -#: cotisations/views.py:171 +#: cotisations/views.py:173 msgid "New invoice" msgstr "Nouvelle facture" -#: cotisations/views.py:216 +#: cotisations/views.py:218 msgid "The cost estimate was created." msgstr "Le devis a été créé." -#: cotisations/views.py:226 +#: cotisations/views.py:228 msgid "New cost estimate" msgstr "Nouveau devis" -#: cotisations/views.py:270 +#: cotisations/views.py:274 msgid "The custom invoice was created." msgstr "La facture personnalisée a été créée." -#: cotisations/views.py:280 +#: cotisations/views.py:284 msgid "New custom invoice" msgstr "Nouvelle facture personnalisée" -#: cotisations/views.py:355 cotisations/views.py:436 +#: cotisations/views.py:359 cotisations/views.py:440 msgid "The invoice was edited." msgstr "La facture a été modifiée." -#: cotisations/views.py:373 cotisations/views.py:568 +#: cotisations/views.py:377 cotisations/views.py:572 msgid "The invoice was deleted." msgstr "La facture a été supprimée." -#: cotisations/views.py:396 +#: cotisations/views.py:400 msgid "The cost estimate was edited." msgstr "Le devis a été modifié." -#: cotisations/views.py:403 +#: cotisations/views.py:407 msgid "Edit cost estimate" msgstr "Modifier le devis" -#: cotisations/views.py:417 +#: cotisations/views.py:421 msgid "An invoice was successfully created from your cost estimate." msgstr "Une facture a bien été créée à partir de votre devis." -#: cotisations/views.py:443 +#: cotisations/views.py:447 msgid "Edit custom invoice" msgstr "Modifier la facture personnalisée" -#: cotisations/views.py:505 +#: cotisations/views.py:509 msgid "The cost estimate was deleted." msgstr "Le devis a été supprimé." -#: cotisations/views.py:508 +#: cotisations/views.py:512 msgid "cost estimate" msgstr "devis" -#: cotisations/views.py:592 +#: cotisations/views.py:596 msgid "The article was created." msgstr "L'article a été créé." -#: cotisations/views.py:597 cotisations/views.py:671 cotisations/views.py:765 +#: cotisations/views.py:601 cotisations/views.py:675 cotisations/views.py:769 msgid "Add" msgstr "Ajouter" -#: cotisations/views.py:598 +#: cotisations/views.py:602 msgid "New article" msgstr "Nouvel article" -#: cotisations/views.py:615 +#: cotisations/views.py:619 msgid "The article was edited." msgstr "L'article a été modifié." -#: cotisations/views.py:620 cotisations/views.py:703 cotisations/views.py:789 +#: cotisations/views.py:624 cotisations/views.py:707 cotisations/views.py:793 msgid "Edit" msgstr "Modifier" -#: cotisations/views.py:621 +#: cotisations/views.py:625 msgid "Edit article" msgstr "Modifier l'article" -#: cotisations/views.py:638 +#: cotisations/views.py:642 msgid "The articles were deleted." msgstr "Les articles ont été supprimés." -#: cotisations/views.py:643 cotisations/views.py:742 cotisations/views.py:827 +#: cotisations/views.py:647 cotisations/views.py:746 cotisations/views.py:831 msgid "Delete" msgstr "Supprimer" -#: cotisations/views.py:644 +#: cotisations/views.py:648 msgid "Delete article" msgstr "Supprimer l'article" -#: cotisations/views.py:665 +#: cotisations/views.py:669 msgid "The payment method was created." msgstr "Le moyen de paiment a été créé." -#: cotisations/views.py:672 +#: cotisations/views.py:676 msgid "New payment method" msgstr "Nouveau moyen de paiement" -#: cotisations/views.py:697 +#: cotisations/views.py:701 msgid "The payment method was edited." msgstr "Le moyen de paiment a été modifié." -#: cotisations/views.py:704 +#: cotisations/views.py:708 msgid "Edit payment method" msgstr "Modifier le moyen de paiement" -#: cotisations/views.py:726 +#: cotisations/views.py:730 #, python-format msgid "The payment method %(method_name)s was deleted." msgstr "Le moyen de paiement %(method_name)s a été supprimé." -#: cotisations/views.py:733 +#: cotisations/views.py:737 #, python-format msgid "" "The payment method %(method_name)s can't be deleted because there are " @@ -1055,32 +1060,32 @@ msgstr "" "Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a " "des factures qui l'utilisent." -#: cotisations/views.py:743 +#: cotisations/views.py:747 msgid "Delete payment method" msgstr "Supprimer le moyen de paiement" -#: cotisations/views.py:760 +#: cotisations/views.py:764 msgid "The bank was created." msgstr "La banque a été créée." -#: cotisations/views.py:766 +#: cotisations/views.py:770 msgid "New bank" msgstr "Nouvelle banque" -#: cotisations/views.py:784 +#: cotisations/views.py:788 msgid "The bank was edited." msgstr "La banque a été modifiée." -#: cotisations/views.py:790 +#: cotisations/views.py:794 msgid "Edit bank" msgstr "Modifier la banque" -#: cotisations/views.py:812 +#: cotisations/views.py:816 #, python-format msgid "The bank %(bank_name)s was deleted." msgstr "La banque %(bank_name)s a été supprimée." -#: cotisations/views.py:818 +#: cotisations/views.py:822 #, python-format msgid "" "The bank %(bank_name)s can't be deleted because there are invoices using it." @@ -1088,22 +1093,22 @@ msgstr "" "La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " "qui l'utilisent." -#: cotisations/views.py:828 +#: cotisations/views.py:832 msgid "Delete bank" msgstr "Supprimer la banque" -#: cotisations/views.py:862 +#: cotisations/views.py:866 msgid "Your changes have been properly taken into account." msgstr "Vos modifications ont correctement été prises en compte." -#: cotisations/views.py:994 +#: cotisations/views.py:998 msgid "You are not allowed to credit your balance." msgstr "Vous n'êtes pas autorisé à créditer votre solde." -#: cotisations/views.py:1025 +#: cotisations/views.py:1029 msgid "Refill your balance" msgstr "Recharger votre solde" -#: cotisations/views.py:1044 +#: cotisations/views.py:1048 msgid "Could not find a voucher for that invoice." -msgstr "Impossible de trouver un reçu pour cette facture." +msgstr "Impossible de trouver un reçu pour cette facture." \ No newline at end of file diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 551d2813..3d7115f4 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,7 +34,7 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: logs/forms.py:40 logs/templates/logs/sidebar.html:53 +#: logs/forms.py:40 msgid "Users" msgstr "Utilisateurs" @@ -54,7 +54,7 @@ msgstr "Accès gracieux" msgid "Bans" msgstr "Bannissements" -#: logs/forms.py:45 logs/views.py:430 +#: logs/forms.py:45 logs/views.py:437 msgid "Topology" msgstr "Topologie" @@ -70,49 +70,49 @@ msgstr "IPv4" msgid "MAC address" msgstr "Adresse MAC" -#: logs/forms.py:101 +#: logs/forms.py:118 msgid "Performed by" msgstr "Effectué(e) par" -#: logs/forms.py:106 +#: logs/forms.py:123 msgid "Action type" msgstr "Type d'action" -#: logs/forms.py:112 logs/forms.py:135 +#: logs/forms.py:129 logs/forms.py:152 #: logs/templates/logs/machine_history.html:37 msgid "Start date" msgstr "Date de début" -#: logs/forms.py:113 logs/forms.py:136 +#: logs/forms.py:130 logs/forms.py:153 #: logs/templates/logs/machine_history.html:38 msgid "End date" msgstr "Date de fin" -#: logs/forms.py:128 logs/templates/logs/search_machine_history.html:38 +#: logs/forms.py:145 logs/templates/logs/search_machine_history.html:38 #: logs/templates/logs/search_stats_logs.html:36 msgid "Search" msgstr "Rechercher" -#: logs/forms.py:132 +#: logs/forms.py:149 msgid "Search type" msgstr "Type de recherche" -#: logs/models.py:310 logs/models.py:552 logs/models.py:585 logs/models.py:798 +#: logs/models.py:359 logs/models.py:666 logs/models.py:699 logs/models.py:938 msgid "None" msgstr "Aucun(e)" -#: logs/models.py:562 logs/models.py:581 logs/models.py:595 logs/models.py:749 -#: logs/models.py:785 logs/models.py:790 logs/models.py:795 logs/models.py:805 -#: logs/views.py:605 +#: logs/models.py:676 logs/models.py:695 logs/models.py:709 logs/models.py:876 +#: logs/models.py:925 logs/models.py:930 logs/models.py:935 logs/models.py:945 +#: logs/views.py:611 msgid "Deleted" msgstr "Supprimé(e)" -#: logs/models.py:569 logs/models.py:574 +#: logs/models.py:683 logs/models.py:688 #: logs/templates/logs/machine_history.html:55 msgid "Unknown" msgstr "Inconnu(e)" -#: logs/models.py:793 +#: logs/models.py:933 msgid "No name" msgstr "Sans nom" @@ -249,7 +249,7 @@ msgid "Statistics" msgstr "Statistiques" #: logs/templates/logs/index.html:32 logs/templates/logs/stats_logs.html:32 -#: logs/views.py:445 +#: logs/views.py:452 msgid "Actions performed" msgstr "Actions effectuées" @@ -279,30 +279,6 @@ msgstr "Rechercher l'historique des machines" msgid "Search events" msgstr "Recherche les évènements" -#: logs/templates/logs/sidebar.html:33 -msgid "Summary" -msgstr "Résumé" - -#: logs/templates/logs/sidebar.html:37 -msgid "Events" -msgstr "Évènements" - -#: logs/templates/logs/sidebar.html:41 -msgid "General" -msgstr "Général" - -#: logs/templates/logs/sidebar.html:45 -msgid "Database" -msgstr "Base de données" - -#: logs/templates/logs/sidebar.html:49 -msgid "Wiring actions" -msgstr "Actions de câblage" - -#: logs/templates/logs/sidebar.html:57 -msgid "Machine history" -msgstr "Historique des machines" - #: logs/templates/logs/stats_general.html:32 msgid "General statistics" msgstr "Statistiques générales" @@ -315,138 +291,156 @@ msgstr "Statistiques sur la base de données" msgid "Statistics about users" msgstr "Statistiques sur les utilisateurs" -#: logs/views.py:192 +#: logs/views.py:198 msgid "Nonexistent revision." msgstr "Révision inexistante." -#: logs/views.py:195 +#: logs/views.py:201 msgid "The action was deleted." msgstr "L'action a été supprimée." -#: logs/views.py:236 +#: logs/views.py:243 msgid "Category" msgstr "Catégorie" -#: logs/views.py:237 +#: logs/views.py:244 msgid "Number of users (members and clubs)" msgstr "Nombre d'utilisateurs (adhérents et clubs)" -#: logs/views.py:238 +#: logs/views.py:245 msgid "Number of members" msgstr "Nombre d'adhérents" -#: logs/views.py:239 +#: logs/views.py:246 msgid "Number of clubs" msgstr "Nombre de clubs" -#: logs/views.py:243 +#: logs/views.py:250 msgid "Activated users" msgstr "Utilisateurs activés" -#: logs/views.py:249 +#: logs/views.py:256 msgid "Disabled users" msgstr "Utilisateurs désactivés" -#: logs/views.py:255 +#: logs/views.py:262 msgid "Archived users" msgstr "Utilisateurs archivés" -#: logs/views.py:261 +#: logs/views.py:268 msgid "Fully archived users" msgstr "Utilisateurs complètement archivés" -#: logs/views.py:271 +#: logs/views.py:278 msgid "Not yet active users" msgstr "Utilisateurs pas encore actifs" -#: logs/views.py:281 +#: logs/views.py:288 msgid "Contributing members" msgstr "Adhérents cotisants" -#: logs/views.py:287 +#: logs/views.py:294 msgid "Users benefiting from a connection" msgstr "Utilisateurs bénéficiant d'une connexion" -#: logs/views.py:293 +#: logs/views.py:300 msgid "Banned users" msgstr "Utilisateurs bannis" -#: logs/views.py:299 +#: logs/views.py:306 msgid "Users benefiting from a free connection" msgstr "Utilisateurs bénéficiant d'une connexion gratuite" -#: logs/views.py:305 +#: logs/views.py:312 msgid "Users with a confirmed email" msgstr "Utilisateurs ayant un mail confirmé" -#: logs/views.py:313 +#: logs/views.py:320 msgid "Users with an unconfirmed email" msgstr "Utilisateurs ayant un mail non confirmé" -#: logs/views.py:325 +#: logs/views.py:332 msgid "Users pending email confirmation" msgstr "Utilisateurs en attente de confirmation du mail" -#: logs/views.py:333 +#: logs/views.py:340 msgid "Active interfaces (with access to the network)" msgstr "Interfaces actives (ayant accès au réseau)" -#: logs/views.py:347 +#: logs/views.py:354 msgid "Active interfaces assigned IPv4" msgstr "Interfaces actives assignées IPv4" -#: logs/views.py:364 +#: logs/views.py:371 msgid "IP range" msgstr "Plage d'IP" -#: logs/views.py:365 +#: logs/views.py:372 msgid "VLAN" msgstr "VLAN" -#: logs/views.py:366 +#: logs/views.py:373 msgid "Total number of IP addresses" msgstr "Nombre total d'adresses IP" -#: logs/views.py:367 +#: logs/views.py:374 msgid "Number of assigned IP addresses" msgstr "Nombre d'adresses IP assignées" -#: logs/views.py:368 +#: logs/views.py:375 msgid "Number of IP address assigned to an activated machine" msgstr "Nombre d'adresses IP assignées à une machine activée" -#: logs/views.py:369 +#: logs/views.py:376 msgid "Number of unassigned IP addresses" msgstr "Nombre d'adresses IP non assignées" -#: logs/views.py:384 +#: logs/views.py:391 msgid "Users (members and clubs)" msgstr "Utilisateurs (adhérents et clubs)" -#: logs/views.py:446 +#: logs/views.py:453 msgid "Number of actions" msgstr "Nombre d'actions" -#: logs/views.py:473 +#: logs/views.py:479 msgid "rights" msgstr "droits" -#: logs/views.py:504 +#: logs/views.py:508 msgid "actions" msgstr "actions" -#: logs/views.py:549 +#: logs/views.py:555 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." -#: logs/views.py:584 +#: logs/views.py:590 msgid "No model found." msgstr "Aucun modèle trouvé." -#: logs/views.py:595 +#: logs/views.py:601 msgid "Nonexistent entry." msgstr "Entrée inexistante." +#~ msgid "Summary" +#~ msgstr "Résumé" + +#~ msgid "Events" +#~ msgstr "Évènements" + +#~ msgid "General" +#~ msgstr "Général" + +#~ msgid "Database" +#~ msgstr "Base de données" + +#~ msgid "Wiring actions" +#~ msgstr "Actions de câblage" + +#~ msgid "Machine history" +#~ msgstr "Historique des machines" + #~ msgid "History of %(title)s" #~ msgstr "Historique de %(title)s" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 1a170c42..f91dcece 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-08-04 22:31+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" @@ -38,7 +38,7 @@ msgstr "Vous n'avez pas le droit de voir cette application." msgid "Machine name" msgstr "Nom de la machine" -#: machines/forms.py:99 machines/templates/machines/aff_machines.html:46 +#: machines/forms.py:99 machines/templates/machines/aff_machines.html:81 msgid "MAC address" msgstr "Adresse MAC" @@ -155,7 +155,7 @@ msgstr "Peut voir un objet machine" msgid "Can change the user of a machine" msgstr "Peut changer l'utilisateur d'une machine" -#: machines/models.py:85 machines/views.py:308 +#: machines/models.py:85 machines/views.py:309 msgid "machine" msgstr "machine" @@ -202,11 +202,11 @@ msgid "You don't have the right to delete a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer une machine d'une autre utilisateur." -#: machines/models.py:245 machines/models.py:2072 +#: machines/models.py:245 machines/models.py:2073 msgid "You don't have the right to view other machines than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." -#: machines/models.py:260 machines/templates/machines/aff_machines.html:53 +#: machines/models.py:260 machines/templates/machines/aff_machines.html:39 msgid "No name" msgstr "Sans nom" @@ -254,8 +254,7 @@ msgstr "Peut voir un objet type d'IP" msgid "Can use all IP types" msgstr "Peut utiliser tous les types d'IP" -#: machines/models.py:438 machines/templates/machines/aff_iptype.html:35 -#: machines/templates/machines/machine.html:108 +#: machines/models.py:438 machines/templates/machines/machine.html:108 msgid "IP type" msgstr "type d'IP" @@ -313,7 +312,7 @@ msgstr "Peut voir un objet VLAN" msgid "VLAN" msgstr "VLAN" -#: machines/models.py:705 machines/templates/machines/sidebar.html:57 +#: machines/models.py:705 msgid "VLANs" msgstr "VLANs" @@ -329,7 +328,7 @@ msgstr "Peut voir un objet dispositif NAS" msgid "NAS device" msgstr "dispositif NAS" -#: machines/models.py:741 machines/templates/machines/sidebar.html:63 +#: machines/models.py:741 msgid "NAS devices" msgstr "dispositifs NAS" @@ -423,7 +422,7 @@ msgid "An extension must begin with a dot." msgstr "Une extension doit commencer par un point." #: machines/models.py:981 machines/models.py:1013 machines/models.py:1045 -#: machines/models.py:1075 machines/models.py:1869 +#: machines/models.py:1075 machines/models.py:1870 msgid "Time To Live (TTL)" msgstr "Temps de vie (TTL)" @@ -524,7 +523,7 @@ msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" #: machines/models.py:1233 machines/templates/machines/machine.html:144 -#: machines/views.py:477 +#: machines/views.py:478 msgid "SSHFP record" msgstr "enregistrement SSHFP" @@ -540,7 +539,7 @@ msgstr "Peut voir un objet interface" msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: machines/models.py:1275 machines/views.py:361 +#: machines/models.py:1275 machines/views.py:362 msgid "interface" msgstr "interface" @@ -608,7 +607,7 @@ msgstr "Peut voir un objet list d'adresses IPv6" msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: machines/models.py:1669 machines/views.py:421 +#: machines/models.py:1669 machines/views.py:422 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" @@ -616,7 +615,7 @@ msgstr "Liste d'adresses IPv6" msgid "IPv6 addresses lists" msgstr "Listes d'adresses IPv6" -#: machines/models.py:1688 machines/models.py:1972 +#: machines/models.py:1688 machines/models.py:1973 msgid "Nonexistent interface." msgstr "Interface inexistante." @@ -660,55 +659,55 @@ msgstr "" "Le préfixe v6 est incorrect et ne correspond pas au type associé à la " "machine." -#: machines/models.py:1862 +#: machines/models.py:1863 msgid "Mandatory and unique, must not contain dots." msgstr "Obligatoire et unique, ne doit pas contenir de points." -#: machines/models.py:1877 +#: machines/models.py:1878 msgid "Can view a domain object" msgstr "Peut voir un objet domaine" -#: machines/models.py:1878 +#: machines/models.py:1879 msgid "Can change the TTL of a domain object" msgstr "Peut changer le TTL d'un objet domaine" -#: machines/models.py:1880 +#: machines/models.py:1881 msgid "domain" msgstr "domaine" -#: machines/models.py:1881 +#: machines/models.py:1882 msgid "domains" msgstr "domaines" -#: machines/models.py:1908 +#: machines/models.py:1909 msgid "You can't create a both A and CNAME record." msgstr "Vous ne pouvez pas créer un enregistrement à la fois A et CNAME." -#: machines/models.py:1911 +#: machines/models.py:1912 msgid "You can't create a CNAME record pointing to itself." msgstr "Vous ne pouvez pas créer un enregistrement CNAME vers lui-même." -#: machines/models.py:1917 +#: machines/models.py:1918 #, python-format msgid "The domain name %s is too long (over 63 characters)." msgstr "Le nom de domaine %s est trop long (plus de 63 caractères)." -#: machines/models.py:1921 +#: machines/models.py:1922 #, python-format msgid "The domain name %s contains forbidden characters." msgstr "Le nom de domaine %s contient des caractères interdits." -#: machines/models.py:1939 +#: machines/models.py:1940 msgid "Invalid extension." msgstr "Extension invalide." -#: machines/models.py:1981 +#: machines/models.py:1982 msgid "You don't have the right to add an alias to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " "utilisateur." -#: machines/models.py:1997 +#: machines/models.py:1998 #, python-format msgid "" "You reached the maximum number of alias that you are allowed to create " @@ -717,164 +716,164 @@ msgstr "" "Vous avez atteint le nombre maximal d'alias que vous pouvez créer vous-même " "(%s)." -#: machines/models.py:2023 +#: machines/models.py:2024 msgid "You don't have the right to edit an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2048 +#: machines/models.py:2049 msgid "" "You don't have the right to delete an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2083 +#: machines/models.py:2084 msgid "You don't have the right to change the domain's TTL." msgstr "Vous n'avez pas le droit de changer le TTL du domaine." -#: machines/models.py:2105 +#: machines/models.py:2106 msgid "Can view an IPv4 addresses list object" msgstr "Peut voir un object liste d'adresses IPv4" -#: machines/models.py:2106 +#: machines/models.py:2107 msgid "IPv4 addresses list" msgstr "Liste d'adresses IPv4" -#: machines/models.py:2107 +#: machines/models.py:2108 msgid "IPv4 addresses lists" msgstr "Listes d'adresses IPv4" -#: machines/models.py:2124 +#: machines/models.py:2125 msgid "The IPv4 address and the range of the IP type don't match." msgstr "L'adresse IPv4 et la plage du type d'IP ne correspondent pas." -#: machines/models.py:2148 +#: machines/models.py:2149 msgid "DHCP server" msgstr "Serveur DHCP" -#: machines/models.py:2149 +#: machines/models.py:2150 msgid "Switches configuration server" msgstr "Serveur de configuration des commutateurs réseau" -#: machines/models.py:2150 +#: machines/models.py:2151 msgid "Recursive DNS server" msgstr "Serveur DNS récursif" -#: machines/models.py:2151 +#: machines/models.py:2152 msgid "NTP server" msgstr "Serveur NTP" -#: machines/models.py:2152 +#: machines/models.py:2153 msgid "RADIUS server" msgstr "Serveur RADIUS" -#: machines/models.py:2153 +#: machines/models.py:2154 msgid "Log server" msgstr "Serveur log" -#: machines/models.py:2154 +#: machines/models.py:2155 msgid "LDAP master server" msgstr "Serveur LDAP maître" -#: machines/models.py:2155 +#: machines/models.py:2156 msgid "LDAP backup server" msgstr "Serveur LDAP de secours" -#: machines/models.py:2156 +#: machines/models.py:2157 msgid "SMTP server" msgstr "Serveur SMTP" -#: machines/models.py:2157 +#: machines/models.py:2158 msgid "postgreSQL server" msgstr "Serveur postgreSQL" -#: machines/models.py:2158 +#: machines/models.py:2159 msgid "mySQL server" msgstr "Serveur mySQL" -#: machines/models.py:2159 +#: machines/models.py:2160 msgid "SQL client" msgstr "Client SQL" -#: machines/models.py:2160 +#: machines/models.py:2161 msgid "Gateway" msgstr "Passerelle" -#: machines/models.py:2168 +#: machines/models.py:2169 msgid "Can view a role object" msgstr "Peut voir un objet rôle" -#: machines/models.py:2169 +#: machines/models.py:2170 msgid "server role" msgstr "rôle de serveur" -#: machines/models.py:2170 +#: machines/models.py:2171 msgid "server roles" msgstr "rôles de serveur" -#: machines/models.py:2209 +#: machines/models.py:2210 msgid "Minimal time before regeneration of the service." msgstr "Temps minimal avant régénération du service." -#: machines/models.py:2213 +#: machines/models.py:2214 msgid "Maximal time before regeneration of the service." msgstr "Temps maximal avant régénération du service." -#: machines/models.py:2218 +#: machines/models.py:2219 msgid "Can view a service object" msgstr "Peut voir un objet service" -#: machines/models.py:2219 +#: machines/models.py:2220 msgid "service to generate (DHCP, DNS, ...)" msgstr "service à générer (DHCP, DNS, ...)" -#: machines/models.py:2220 +#: machines/models.py:2221 msgid "services to generate (DHCP, DNS, ...)" msgstr "services à générer (DHCP, DNS, ...)" -#: machines/models.py:2277 +#: machines/models.py:2278 msgid "Can view a service server link object" msgstr "Peut voir un objet lien service serveur" -#: machines/models.py:2279 +#: machines/models.py:2280 msgid "link between service and server" msgstr "lien entre service et serveur" -#: machines/models.py:2280 +#: machines/models.py:2281 msgid "links between service and server" msgstr "liens entre service et serveur" -#: machines/models.py:2327 +#: machines/models.py:2328 msgid "Name of the ports configuration" msgstr "Nom de la configuration de ports" -#: machines/models.py:2332 +#: machines/models.py:2333 msgid "Can view a ports opening list object" msgstr "Peut voir un objet liste d'ouverture de ports" -#: machines/models.py:2334 +#: machines/models.py:2335 msgid "ports opening list" msgstr "liste d'ouverture de ports" -#: machines/models.py:2335 +#: machines/models.py:2336 msgid "ports opening lists" msgstr "listes d'ouverture de ports" -#: machines/models.py:2350 +#: machines/models.py:2351 msgid "You don't have the right to delete a ports opening list." msgstr "Vous n'avez pas le droit de supprimer une liste d'ouverture de ports." -#: machines/models.py:2354 +#: machines/models.py:2355 msgid "This ports opening list is used." msgstr "Cette liste d'ouverture de ports est utilisée." -#: machines/models.py:2418 +#: machines/models.py:2419 msgid "ports opening" msgstr "ouverture de ports" -#: machines/models.py:2419 +#: machines/models.py:2420 msgid "ports openings" msgstr "ouvertures de ports" @@ -903,14 +902,12 @@ msgid "Record" msgstr "Enregistrement" #: machines/templates/machines/aff_extension.html:34 -#: machines/templates/machines/aff_iptype.html:36 #: machines/templates/machines/aff_srv.html:34 #: machines/templates/machines/machine.html:116 msgid "Extension" msgstr "Extension" #: machines/templates/machines/aff_extension.html:35 -#: machines/templates/machines/aff_iptype.html:37 msgid "\"infra\" right required" msgstr "droit « infra » requis" @@ -918,23 +915,31 @@ msgstr "droit « infra » requis" msgid "DNSSEC" msgstr "DNSSEC" -#: machines/templates/machines/aff_iptype.html:38 +#: machines/templates/machines/aff_iptype.html:48 +msgid "Infra right required" +msgstr "droit « infra » requis" + +#: machines/templates/machines/aff_iptype.html:50 +msgid "Infra right not required" +msgstr "droit « infra » non requis" + +#: machines/templates/machines/aff_iptype.html:66 msgid "IPv4 range" msgstr "Plage IPv4" -#: machines/templates/machines/aff_iptype.html:39 +#: machines/templates/machines/aff_iptype.html:73 msgid "v6 prefix" msgstr "Préfixe v6" -#: machines/templates/machines/aff_iptype.html:40 +#: machines/templates/machines/aff_iptype.html:80 msgid "DNSSEC reverse v4/v6" msgstr "DNSSEC inverse v4/v6" -#: machines/templates/machines/aff_iptype.html:41 +#: machines/templates/machines/aff_iptype.html:85 msgid "On VLAN(s)" msgstr "Sur VLAN(s)" -#: machines/templates/machines/aff_iptype.html:42 +#: machines/templates/machines/aff_iptype.html:90 msgid "Default ports opening" msgstr "Ouverture de ports par défaut" @@ -947,74 +952,60 @@ msgid "SLAAC" msgstr "SLAAC" #: machines/templates/machines/aff_ipv6.html:34 -#, fuzzy -#| msgid "Actions" msgid "Active" -msgstr "Actions" +msgstr "Actif" -#: machines/templates/machines/aff_machines.html:43 -msgid "DNS name" -msgstr "Nom DNS" - -#: machines/templates/machines/aff_machines.html:45 -msgid "Type" -msgstr "Type" - -#: machines/templates/machines/aff_machines.html:47 -msgid "IP address" -msgstr "Adresse IP" - -#: machines/templates/machines/aff_machines.html:48 -msgid "Actions" -msgstr "Actions" - -#: machines/templates/machines/aff_machines.html:54 -msgid "View the profile" -msgstr "Voir le profil" - -#: machines/templates/machines/aff_machines.html:58 -msgid "Deactivated" -msgstr "Désactivée" - -#: machines/templates/machines/aff_machines.html:67 +#: machines/templates/machines/aff_machines.html:46 msgid "Create an interface" msgstr "Créer une interface" -#: machines/templates/machines/aff_machines.html:84 -msgid "Display the aliases" -msgstr "Afficher les alias" +#: machines/templates/machines/aff_machines.html:62 +msgid "DNS name" +msgstr "Nom DNS" -#: machines/templates/machines/aff_machines.html:98 +#: machines/templates/machines/aff_machines.html:69 +msgid "Show aliases" +msgstr "Afficher les Alias" + +#: machines/templates/machines/aff_machines.html:76 +msgid "Type" +msgstr "Type" + +#: machines/templates/machines/aff_machines.html:87 msgid "Display the vendor" msgstr "Afficher le constructeur" -#: machines/templates/machines/aff_machines.html:109 -msgid "Display the IPv6 address" +#: machines/templates/machines/aff_machines.html:98 +msgid "Display IPv6" msgstr "Afficher les adresses IPv6" -#: machines/templates/machines/aff_machines.html:126 machines/views.py:290 -#: machines/views.py:405 machines/views.py:461 machines/views.py:517 -#: machines/views.py:586 machines/views.py:649 machines/views.py:715 -#: machines/views.py:767 machines/views.py:819 machines/views.py:871 -#: machines/views.py:926 machines/views.py:978 machines/views.py:1040 -#: machines/views.py:1096 machines/views.py:1148 machines/views.py:1214 -#: machines/views.py:1266 machines/views.py:1584 +#: machines/templates/machines/aff_machines.html:101 +msgid "No IPv6" +msgstr "Pas d'IPv6" + +#: machines/templates/machines/aff_machines.html:116 machines/views.py:291 +#: machines/views.py:406 machines/views.py:462 machines/views.py:518 +#: machines/views.py:587 machines/views.py:650 machines/views.py:716 +#: machines/views.py:768 machines/views.py:820 machines/views.py:872 +#: machines/views.py:927 machines/views.py:979 machines/views.py:1041 +#: machines/views.py:1097 machines/views.py:1149 machines/views.py:1215 +#: machines/views.py:1267 machines/views.py:1621 msgid "Edit" msgstr "Modifier" -#: machines/templates/machines/aff_machines.html:134 +#: machines/templates/machines/aff_machines.html:124 msgid "Manage the aliases" msgstr "Gérer les alias" -#: machines/templates/machines/aff_machines.html:142 +#: machines/templates/machines/aff_machines.html:132 msgid "Manage the IPv6 addresses" msgstr "Gérer les adresses IPv6" -#: machines/templates/machines/aff_machines.html:150 +#: machines/templates/machines/aff_machines.html:140 msgid "Manage the SSH fingerprints" msgstr "Gérer les empreintes SSH" -#: machines/templates/machines/aff_machines.html:158 +#: machines/templates/machines/aff_machines.html:148 msgid "Manage the ports configuration" msgstr "Gérer les configuration de ports" @@ -1036,7 +1027,6 @@ msgstr "Priorité" #: machines/templates/machines/aff_nas.html:33 #: machines/templates/machines/aff_soa.html:32 #: machines/templates/machines/aff_vlan.html:34 -#: machines/templates/machines/index_portlist.html:20 msgid "Name" msgstr "Nom" @@ -1060,6 +1050,54 @@ msgstr "Capture automatique de l'adresse MAC" msgid "Authoritarian interface for the concerned zone" msgstr "Interface authoritaire pour la zone concernée" +#: machines/templates/machines/aff_portlist.html:58 +msgid "TCP (input)" +msgstr "TCP (entrée)" + +#: machines/templates/machines/aff_portlist.html:63 +msgid "TCP (output)" +msgstr "TCP (sortie)" + +#: machines/templates/machines/aff_portlist.html:68 +msgid "UDP (input)" +msgstr "UDP (entrée)" + +#: machines/templates/machines/aff_portlist.html:73 +msgid "UDP (output)" +msgstr "UDP (sortie)" + +#: machines/templates/machines/aff_portlist.html:84 +#: machines/templates/machines/aff_profil.html:34 +#: machines/templates/machines/edit_portlist.html:29 +#: machines/templates/machines/index.html:29 +#: machines/templates/machines/index.html:32 +#: machines/templates/machines/index_alias.html:29 +#: machines/templates/machines/index_extension.html:30 +#: machines/templates/machines/index_iptype.html:30 +#: machines/templates/machines/index_ipv6.html:30 +#: machines/templates/machines/index_machinetype.html:31 +#: machines/templates/machines/index_nas.html:31 +#: machines/templates/machines/index_portlist.html:8 +#: machines/templates/machines/index_role.html:30 +#: machines/templates/machines/index_service.html:30 +#: machines/templates/machines/index_sshfp.html:28 +#: machines/templates/machines/index_vlan.html:30 +#: machines/templates/machines/machine.html:31 +msgid "Machines" +msgstr "Machines" + +#: machines/templates/machines/aff_portlist.html:94 +msgid "None" +msgstr "Aucun" + +#: machines/templates/machines/aff_profil.html:42 +msgid "Add a machine" +msgstr "Ajouter une machine" + +#: machines/templates/machines/aff_profil.html:49 +msgid "No machines" +msgstr "Pas de machines" + #: machines/templates/machines/aff_role.html:33 msgid "Role name" msgstr "Nom du rôle" @@ -1164,7 +1202,6 @@ msgid "ID" msgstr "ID" #: machines/templates/machines/aff_vlan.html:36 -#: machines/templates/machines/sidebar.html:51 msgid "IP ranges" msgstr "Plages d'IP" @@ -1186,26 +1223,6 @@ msgstr "" msgid "Confirm" msgstr "Confirmer" -#: machines/templates/machines/edit_portlist.html:29 -#: machines/templates/machines/index.html:29 -#: machines/templates/machines/index.html:32 -#: machines/templates/machines/index_alias.html:29 -#: machines/templates/machines/index_extension.html:30 -#: machines/templates/machines/index_iptype.html:30 -#: machines/templates/machines/index_ipv6.html:30 -#: machines/templates/machines/index_machinetype.html:31 -#: machines/templates/machines/index_nas.html:31 -#: machines/templates/machines/index_portlist.html:8 -#: machines/templates/machines/index_portlist.html:25 -#: machines/templates/machines/index_role.html:30 -#: machines/templates/machines/index_service.html:30 -#: machines/templates/machines/index_sshfp.html:28 -#: machines/templates/machines/index_vlan.html:30 -#: machines/templates/machines/machine.html:31 -#: machines/templates/machines/sidebar.html:33 -msgid "Machines" -msgstr "Machines" - #: machines/templates/machines/edit_portlist.html:50 msgid "Add a port" msgstr "Ajouter un port" @@ -1306,15 +1323,15 @@ msgstr "Ajouter un enregistrement SRV" msgid "Delete one or several SRV records" msgstr "Supprimer un ou plusieurs enregistrements SRV" -#: machines/templates/machines/index_iptype.html:33 +#: machines/templates/machines/index_iptype.html:34 msgid "List of IP types" msgstr "Liste des types d'IP" -#: machines/templates/machines/index_iptype.html:36 +#: machines/templates/machines/index_iptype.html:37 msgid "Add an IP type" msgstr "Ajouter un type d'IP" -#: machines/templates/machines/index_iptype.html:40 +#: machines/templates/machines/index_iptype.html:41 msgid "Delete one or several IP types" msgstr "Supprimer un ou plusieurs types d'IP" @@ -1369,22 +1386,6 @@ msgstr "Liste des configurations de ports" msgid "Add a configuration" msgstr "Ajouter une configuration" -#: machines/templates/machines/index_portlist.html:21 -msgid "TCP (input)" -msgstr "TCP (entrée)" - -#: machines/templates/machines/index_portlist.html:22 -msgid "TCP (output)" -msgstr "TCP (sortie)" - -#: machines/templates/machines/index_portlist.html:23 -msgid "UDP (input)" -msgstr "UDP (entrée)" - -#: machines/templates/machines/index_portlist.html:24 -msgid "UDP (output)" -msgstr "UDP (sortie)" - #: machines/templates/machines/index_role.html:33 msgid "List of roles" msgstr "Liste des rôles" @@ -1453,101 +1454,81 @@ msgstr "Alias" msgid "IPv6 address" msgstr "Adresse IPv6" -#: machines/templates/machines/sidebar.html:39 -msgid "Machine types" -msgstr "Types de machine" - -#: machines/templates/machines/sidebar.html:45 -msgid "Extensions and zones" -msgstr "Extensions et zones" - -#: machines/templates/machines/sidebar.html:69 -msgid "Services (DHCP, DNS, ...)" -msgstr "Services (DHCP, DNS, ...)" - -#: machines/templates/machines/sidebar.html:75 -msgid "Server roles" -msgstr "Rôles de serveur" - -#: machines/templates/machines/sidebar.html:81 -msgid "Ports openings" -msgstr "Ouvertures de ports" - -#: machines/views.py:143 +#: machines/views.py:144 msgid "Select a machine type first." msgstr "Sélectionnez un type de machine d'abord." -#: machines/views.py:235 +#: machines/views.py:236 msgid "The machine was created." msgstr "La machine a été créée." -#: machines/views.py:244 machines/views.py:340 machines/views.py:381 -#: machines/views.py:439 machines/views.py:494 machines/views.py:566 -#: machines/views.py:632 machines/views.py:698 machines/views.py:750 -#: machines/views.py:802 machines/views.py:854 machines/views.py:909 -#: machines/views.py:961 machines/views.py:1018 machines/views.py:1079 -#: machines/views.py:1131 machines/views.py:1197 machines/views.py:1249 +#: machines/views.py:245 machines/views.py:341 machines/views.py:382 +#: machines/views.py:440 machines/views.py:495 machines/views.py:567 +#: machines/views.py:633 machines/views.py:699 machines/views.py:751 +#: machines/views.py:803 machines/views.py:855 machines/views.py:910 +#: machines/views.py:962 machines/views.py:1019 machines/views.py:1080 +#: machines/views.py:1132 machines/views.py:1198 machines/views.py:1250 msgid "Add" msgstr "Ajouter" -#: machines/views.py:276 +#: machines/views.py:277 msgid "The machine was edited." msgstr "La machine a été modifiée." -#: machines/views.py:303 +#: machines/views.py:304 msgid "The machine was deleted." msgstr "La machine a été supprimée." -#: machines/views.py:330 +#: machines/views.py:331 msgid "The interface was created." msgstr "L'interface a été créée." -#: machines/views.py:356 +#: machines/views.py:357 msgid "The interface was deleted." msgstr "L'interface a été supprimée." -#: machines/views.py:376 +#: machines/views.py:377 msgid "The IPv6 addresses list was created." msgstr "La liste d'adresses IPv6 a été créée." -#: machines/views.py:397 +#: machines/views.py:398 msgid "The IPv6 addresses list was edited." msgstr "La liste d'adresses IPv6 a été modifiée." -#: machines/views.py:416 +#: machines/views.py:417 msgid "The IPv6 addresses list was deleted." msgstr "La liste d'adresses IPv6 a été supprimée." -#: machines/views.py:434 +#: machines/views.py:435 msgid "The SSHFP record was created." msgstr "L'enregistrement SSHFP a été créé." -#: machines/views.py:453 +#: machines/views.py:454 msgid "The SSHFP record was edited." msgstr "L'enregistrement SSHFP a été modifié." -#: machines/views.py:472 +#: machines/views.py:473 msgid "The SSHFP record was deleted." msgstr "L'enregistrement SSHFP a été supprimé." -#: machines/views.py:491 +#: machines/views.py:492 msgid "The IP type was created." msgstr "Le type d'IP a été créé." -#: machines/views.py:512 +#: machines/views.py:513 msgid "The IP type was edited." msgstr "Le type d'IP a été modifié." -#: machines/views.py:514 +#: machines/views.py:515 msgid "This IP type change would create duplicated domains" msgstr "" "Ce changement de type d'IP causerait des duplications de noms de domaine" -#: machines/views.py:536 +#: machines/views.py:537 msgid "The IP type was deleted." msgstr "Le type d'IP a été supprimé." -#: machines/views.py:542 +#: machines/views.py:543 #, python-format msgid "" "The IP type %s is assigned to at least one machine, you can't delete it." @@ -1555,33 +1536,33 @@ msgstr "" "Le type d'IP %s est assigné à au moins une machine, vous ne pouvez pas le " "supprimer." -#: machines/views.py:550 machines/views.py:616 machines/views.py:682 -#: machines/views.py:736 machines/views.py:788 machines/views.py:840 -#: machines/views.py:893 machines/views.py:947 machines/views.py:999 -#: machines/views.py:1063 machines/views.py:1117 machines/views.py:1172 -#: machines/views.py:1235 machines/views.py:1287 +#: machines/views.py:551 machines/views.py:617 machines/views.py:683 +#: machines/views.py:737 machines/views.py:789 machines/views.py:841 +#: machines/views.py:894 machines/views.py:948 machines/views.py:1000 +#: machines/views.py:1064 machines/views.py:1118 machines/views.py:1173 +#: machines/views.py:1236 machines/views.py:1288 msgid "Delete" msgstr "Supprimer" -#: machines/views.py:563 +#: machines/views.py:564 msgid "The machine type was created." msgstr "Le type de machine a été créé." -#: machines/views.py:581 +#: machines/views.py:582 msgid "The machine type was edited." msgstr "Le type de machine a été modifié." -#: machines/views.py:583 +#: machines/views.py:584 msgid "This machine type change would create duplicated domains" msgstr "" "Ce changement de type de machine causerait des duplications de noms de " "domaines" -#: machines/views.py:602 +#: machines/views.py:603 msgid "The machine type was deleted." msgstr "Le type de machine a été supprimé." -#: machines/views.py:608 +#: machines/views.py:609 #, python-format msgid "" "The machine type %s is assigned to at least one machine, you can't delete it." @@ -1589,226 +1570,226 @@ msgstr "" "Le type de machine %s est assigné à au moins un machine, vous ne pouvez pas " "le supprimer." -#: machines/views.py:629 +#: machines/views.py:630 msgid "The extension was created." msgstr "L'extension a été créée." -#: machines/views.py:646 +#: machines/views.py:647 msgid "The extension was edited." msgstr "L'extension a été modifiée." -#: machines/views.py:665 +#: machines/views.py:666 msgid "The extension was deleted." msgstr "L'extension a été supprimée." -#: machines/views.py:671 +#: machines/views.py:672 #, python-format msgid "The extension %s is assigned to following %s : %s, you can't delete it." msgstr "" "L'extension %s est assignée aux %s suivants : %s , vous ne pouvez pas le " "supprimer." -#: machines/views.py:695 +#: machines/views.py:696 msgid "The SOA record was created." msgstr "L'enregistrement SOA a été créé." -#: machines/views.py:712 +#: machines/views.py:713 msgid "The SOA record was edited." msgstr "L'enregistrement SOA a été modifié." -#: machines/views.py:729 +#: machines/views.py:730 msgid "The SOA record was deleted." msgstr "L'enregistrement SOA a été supprimé." -#: machines/views.py:732 +#: machines/views.py:733 #, python-format msgid "Error: the SOA record %s can't be deleted." msgstr "Erreur : l'enregistrement SOA %s ne peut pas être supprimé." -#: machines/views.py:747 +#: machines/views.py:748 msgid "The MX record was created." msgstr "L'enregistrement MX a été créé." -#: machines/views.py:764 +#: machines/views.py:765 msgid "The MX record was edited." msgstr "L'enregistrement MX a été modifié." -#: machines/views.py:781 +#: machines/views.py:782 msgid "The MX record was deleted." msgstr "L'enregistrement MX a été supprimé." -#: machines/views.py:784 +#: machines/views.py:785 #, python-format msgid "Error: the MX record %s can't be deleted." msgstr "Erreur : l'enregistrement MX %s ne peut pas être supprimé." -#: machines/views.py:799 +#: machines/views.py:800 msgid "The NS record was created." msgstr "L'enregistrement NS a été créé." -#: machines/views.py:816 +#: machines/views.py:817 msgid "The NS record was edited." msgstr "L'enregistrement NS a été modifié." -#: machines/views.py:833 +#: machines/views.py:834 msgid "The NS record was deleted." msgstr "L'enregistrement NS a été supprimé." -#: machines/views.py:836 +#: machines/views.py:837 #, python-format msgid "Error: the NS record %s can't be deleted." msgstr "Erreur : l'enregistrement NS %s ne peut pas être supprimé." -#: machines/views.py:851 +#: machines/views.py:852 msgid "The DNAME record was created." msgstr "L'enregistrement DNAME a été créé." -#: machines/views.py:868 +#: machines/views.py:869 msgid "The DNAME record was edited." msgstr "L'enregistrement DNAME a été modifié." -#: machines/views.py:885 +#: machines/views.py:886 msgid "The DNAME record was deleted." msgstr "L'enregistrement DNAME a été supprimé." -#: machines/views.py:889 +#: machines/views.py:890 #, python-format msgid "Error: the DNAME record %s can't be deleted." msgstr "Erreur : l'enregistrement DNAME %s ne peut pas être supprimé." -#: machines/views.py:906 +#: machines/views.py:907 msgid "The TXT record was created." msgstr "L'enregistrement TXT a été créé." -#: machines/views.py:923 +#: machines/views.py:924 msgid "The TXT record was edited." msgstr "L'enregistrement TXT a été modifié." -#: machines/views.py:940 +#: machines/views.py:941 msgid "The TXT record was deleted." msgstr "L'enregistrement TXT a été supprimé." -#: machines/views.py:943 +#: machines/views.py:944 #, python-format msgid "Error: the TXT record %s can't be deleted." msgstr "Erreur : l'enregistrement %s ne peut pas être supprimé." -#: machines/views.py:958 +#: machines/views.py:959 msgid "The SRV record was created." msgstr "L'enregistrement SRV a été créé." -#: machines/views.py:975 +#: machines/views.py:976 msgid "The SRV record was edited." msgstr "L'enregistrement SRV a été modifié." -#: machines/views.py:992 +#: machines/views.py:993 msgid "The SRV record was deleted." msgstr "L'enregistrement SRV a été supprimé." -#: machines/views.py:995 +#: machines/views.py:996 #, python-format msgid "Error: the SRV record %s can't be deleted." msgstr "Erreur : l'enregistrement SRV %s ne peut pas être supprimé." -#: machines/views.py:1013 +#: machines/views.py:1014 msgid "The alias was created." msgstr "L'alias a été créé." -#: machines/views.py:1032 +#: machines/views.py:1033 msgid "The alias was edited." msgstr "L'alias a été modifié." -#: machines/views.py:1054 +#: machines/views.py:1055 #, python-format msgid "The alias %s was deleted." msgstr "L'alias %s a été supprimé." -#: machines/views.py:1057 +#: machines/views.py:1058 #, python-format msgid "Error: the alias %s can't be deleted." msgstr "Erreur : l'alias %s ne peut pas être supprimé." -#: machines/views.py:1076 +#: machines/views.py:1077 msgid "The role was created." msgstr "Le rôle a été créé." -#: machines/views.py:1093 +#: machines/views.py:1094 msgid "The role was edited." msgstr "Le rôle a été modifié." -#: machines/views.py:1110 +#: machines/views.py:1111 msgid "The role was deleted." msgstr "Le rôle a été supprimé." -#: machines/views.py:1113 +#: machines/views.py:1114 #, python-format msgid "Error: the role %s can't be deleted." msgstr "Erreur : le rôle %s ne peut pas être supprimé." -#: machines/views.py:1128 +#: machines/views.py:1129 msgid "The service was created." msgstr "Le service a été créé." -#: machines/views.py:1145 +#: machines/views.py:1146 msgid "The service was edited." msgstr "Le service a été modifié." -#: machines/views.py:1164 +#: machines/views.py:1165 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: machines/views.py:1168 +#: machines/views.py:1169 #, python-format msgid "Error: the service %s can't be deleted." msgstr "Erreur : le service %s ne peut pas être supprimé." -#: machines/views.py:1194 +#: machines/views.py:1195 msgid "The VLAN was created." msgstr "Le VLAN a été créé." -#: machines/views.py:1211 +#: machines/views.py:1212 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: machines/views.py:1228 +#: machines/views.py:1229 msgid "The VLAN was deleted." msgstr "Le VLAN a été supprimé." -#: machines/views.py:1231 +#: machines/views.py:1232 #, python-format msgid "Error: the VLAN %s can't be deleted." msgstr "Erreur : le VLAN %s ne peut pas être supprimé." -#: machines/views.py:1246 +#: machines/views.py:1247 msgid "The NAS device was created." msgstr "Le dispositif NAS a été créé." -#: machines/views.py:1263 +#: machines/views.py:1264 msgid "The NAS device was edited." msgstr "Le dispositif NAS a été modifié." -#: machines/views.py:1280 +#: machines/views.py:1281 msgid "The NAS device was deleted." msgstr "Le dispositif NAS a été supprimé." -#: machines/views.py:1283 +#: machines/views.py:1284 #, python-format msgid "Error: the NAS device %s can't be deleted." msgstr "Erreur : le dispositif NAS %s ne peut pas être supprimé." -#: machines/views.py:1510 +#: machines/views.py:1547 msgid "The ports list was edited." msgstr "La liste de ports a été modifiée." -#: machines/views.py:1524 +#: machines/views.py:1561 msgid "The ports list was deleted." msgstr "La liste de ports a été supprimée." -#: machines/views.py:1549 +#: machines/views.py:1586 msgid "The ports list was created." msgstr "La liste de ports a été créée." -#: machines/views.py:1570 +#: machines/views.py:1607 msgid "" "Warning: the IP address is not public, the opening won't have any effect in " "v4." @@ -1816,10 +1797,6 @@ msgstr "" "Attention : l'adresse IP n'est pas publique, l'ouverture n'aura pas d'effet " "en v4." -#: machines/views.py:1581 +#: machines/views.py:1618 msgid "The ports configuration was edited." msgstr "La configuration de ports a été modifiée." - -#~ msgid "You don't have the right to edit interfaces of another user." -#~ msgstr "" -#~ "Vous n'avez pas le droit de modifier une interface d'un autre utilisateur." diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 6e271271..cf070860 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,79 +21,67 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: multi_op/forms.py:43 +#: multi_op/forms.py:44 msgid "Dormitory" msgstr "Résidence" -#: multi_op/preferences/models.py:37 +#: multi_op/preferences/models.py:41 msgid "enabled dorm" msgstr "résidence activée" -#: multi_op/preferences/models.py:41 +#: multi_op/preferences/models.py:45 msgid "dormitories preferences" msgstr "préférences de résidences" -#: multi_op/templates/multi_op/aff_room_state.html:36 -msgid "Room" -msgstr "Chambre" - -#: multi_op/templates/multi_op/aff_room_state.html:37 -msgid "Building" -msgstr "Bâtiment" - -#: multi_op/templates/multi_op/aff_room_state.html:40 -msgid "Connnected to" -msgstr "Connectée à" - -#: multi_op/templates/multi_op/aff_room_state.html:41 +#: multi_op/templates/multi_op/aff_room_state.html:54 msgid "User" msgstr "Utilisateur" -#: multi_op/templates/multi_op/aff_room_state.html:42 -msgid "Details" -msgstr "Détails" +#: multi_op/templates/multi_op/aff_room_state.html:55 +msgid "Connected to" +msgstr "Connectée à" -#: multi_op/templates/multi_op/aff_room_state.html:43 +#: multi_op/templates/multi_op/aff_room_state.html:56 msgid "End of subscription on" msgstr "Fin de cotisation le" -#: multi_op/templates/multi_op/aff_room_state.html:44 +#: multi_op/templates/multi_op/aff_room_state.html:57 msgid "Internet access" msgstr "Accès Internet" -#: multi_op/templates/multi_op/aff_room_state.html:45 -msgid "Action" -msgstr "Action" - -#: multi_op/templates/multi_op/aff_room_state.html:52 -msgid "Other operator" -msgstr "Autre opérateur" - -#: multi_op/templates/multi_op/aff_room_state.html:53 +#: multi_op/templates/multi_op/aff_room_state.html:65 msgid "None" msgstr "Aucun" -#: multi_op/templates/multi_op/aff_room_state.html:55 +#: multi_op/templates/multi_op/aff_room_state.html:72 +msgid "Other operator" +msgstr "Autre opérateur" + +#: multi_op/templates/multi_op/aff_room_state.html:84 msgid "Non member" msgstr "Non adhérent" -#: multi_op/templates/multi_op/aff_room_state.html:58 +#: multi_op/templates/multi_op/aff_room_state.html:90 msgid "Active" msgstr "Actif" -#: multi_op/templates/multi_op/aff_room_state.html:60 +#: multi_op/templates/multi_op/aff_room_state.html:92 msgid "Disabled" msgstr "Désactivé" +#: multi_op/templates/multi_op/aff_room_state.html:101 +msgid "Details: " +msgstr "Détails :" + #: multi_op/templates/multi_op/index_room_state.html:30 msgid "Multiple operators" msgstr "Opérateurs multiples" @@ -106,23 +94,36 @@ msgstr "Connexions de chambre" msgid "Select dormitory" msgstr "Sélectionnez la résidence" -#: multi_op/templates/multi_op/navbar.html:2 -msgid "Manage the operators" -msgstr "Gérer les opérateurs" +#: multi_op/templates/multi_op/navbar.html:3 +msgid "Multi op" +msgstr "Opérateurs multiples" -#: multi_op/templates/multi_op/sidebar.html:31 +#: multi_op/templates/multi_op/navbar.html:7 msgid "Room connections state" msgstr "État des connexions de chambre" -#: multi_op/templates/multi_op/sidebar.html:35 +#: multi_op/templates/multi_op/navbar.html:10 msgid "Sockets to connect" msgstr "Prises à connecter" -#: multi_op/templates/multi_op/sidebar.html:39 +#: multi_op/templates/multi_op/navbar.html:13 msgid "Sockets to disconnect" msgstr "Prises à déconnecter" -#: multi_op/views.py:169 +#: multi_op/templates/multi_op/preferences.html:7 +msgid "Multi Op" +msgstr "Opérateurs Multiples" + +#: multi_op/templates/multi_op/preferences.html:14 +msgid "Edit" +msgstr "Modifier" + +#: multi_op/templates/multi_op/preferences.html:21 +msgid "Enabled Dorm" +msgstr "Résidence Activée" + +#: multi_op/views.py:194 #, python-format msgid "The room %s was disconnected." msgstr "La chambre %s a été déconnectée." + diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 77df0a3d..4afcf4fd 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -35,7 +35,7 @@ msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." #: preferences/forms.py:65 -#: preferences/templates/preferences/display_preferences.html:149 +#: preferences/templates/preferences/display_preferences.html:150 msgid "Telephone number required" msgstr "Numéro de téléphone requis" @@ -68,13 +68,13 @@ msgid "Self room policy" msgstr "Automodification de la chambre" #: preferences/forms.py:73 -#: preferences/templates/preferences/display_preferences.html:84 +#: preferences/templates/preferences/display_preferences.html:85 msgid "Local email accounts enabled" msgstr "Comptes mail locaux activés" #: preferences/forms.py:74 -#: preferences/templates/preferences/display_preferences.html:86 -#: preferences/templates/preferences/display_preferences.html:166 +#: preferences/templates/preferences/display_preferences.html:87 +#: preferences/templates/preferences/display_preferences.html:167 msgid "Local email domain" msgstr "Domaine de mail local" @@ -91,7 +91,7 @@ msgid "Disabled email not yet confirmed" msgstr "Désactivation sans confirmation de l'adresse mail" #: preferences/forms.py:78 -#: preferences/templates/preferences/display_preferences.html:121 +#: preferences/templates/preferences/display_preferences.html:122 msgid "Self registration" msgstr "Autoinscription" @@ -114,12 +114,12 @@ msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" #: preferences/forms.py:98 -#: preferences/templates/preferences/display_preferences.html:196 +#: preferences/templates/preferences/display_preferences.html:197 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" #: preferences/forms.py:101 -#: preferences/templates/preferences/display_preferences.html:200 +#: preferences/templates/preferences/display_preferences.html:201 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" @@ -131,154 +131,148 @@ msgstr "Mode IPv6" msgid "Can create a machine" msgstr "Peut créer une machine" -#: preferences/forms.py:147 +#: preferences/forms.py:145 msgid "General message in French" msgstr "Message général en français" -#: preferences/forms.py:148 +#: preferences/forms.py:146 msgid "General message in English" msgstr "Message général en anglais" -#: preferences/forms.py:150 -#: preferences/templates/preferences/display_preferences.html:58 +#: preferences/forms.py:148 +#: preferences/templates/preferences/display_preferences.html:59 msgid "Number of results displayed when searching" msgstr "Nombre de résultats affichés lors de la recherche" -#: preferences/forms.py:153 +#: preferences/forms.py:151 msgid "Number of items per page, standard size (e.g. users)" msgstr "Nombre d'éléments par page, taille standard (ex : utilisateurs)" -#: preferences/forms.py:156 +#: preferences/forms.py:154 msgid "Number of items per page, large size (e.g. machines)" msgstr "Nombre d'éléments par page, taille importante (ex : machines)" -#: preferences/forms.py:159 -#: preferences/templates/preferences/display_preferences.html:66 +#: preferences/forms.py:157 +#: preferences/templates/preferences/display_preferences.html:67 msgid "Time before expiration of the reset password link (in hours)" msgstr "" "Temps avant expiration du lien de réinitialisation de mot de passe (en " "heures)" -#: preferences/forms.py:161 -#: preferences/templates/preferences/display_preferences.html:52 +#: preferences/forms.py:159 +#: preferences/templates/preferences/display_preferences.html:53 msgid "Website name" msgstr "Nom du site" -#: preferences/forms.py:162 -#: preferences/templates/preferences/display_preferences.html:54 +#: preferences/forms.py:160 +#: preferences/templates/preferences/display_preferences.html:55 msgid "Email address for automatic emailing" msgstr "Adresse mail pour les mails automatiques" -#: preferences/forms.py:163 -#: preferences/templates/preferences/display_preferences.html:76 +#: preferences/forms.py:161 +#: preferences/templates/preferences/display_preferences.html:77 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: preferences/forms.py:164 -#: preferences/templates/preferences/display_preferences.html:78 +#: preferences/forms.py:162 +#: preferences/templates/preferences/display_preferences.html:79 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: preferences/forms.py:177 +#: preferences/forms.py:175 msgid "Organisation name" msgstr "Nom de l'association" -#: preferences/forms.py:178 -#: preferences/templates/preferences/display_preferences.html:329 +#: preferences/forms.py:176 +#: preferences/templates/preferences/display_preferences.html:330 msgid "SIRET number" msgstr "Numéro SIRET" -#: preferences/forms.py:179 +#: preferences/forms.py:177 msgid "Address (line 1)" msgstr "Adresse (ligne 1)" -#: preferences/forms.py:180 +#: preferences/forms.py:178 msgid "Address (line 2)" msgstr "Adresse (ligne 2)" -#: preferences/forms.py:181 -#: preferences/templates/preferences/display_preferences.html:337 +#: preferences/forms.py:179 +#: preferences/templates/preferences/display_preferences.html:338 msgid "Contact email address" msgstr "Adresse mail de contact" -#: preferences/forms.py:182 -#: preferences/templates/preferences/display_preferences.html:341 +#: preferences/forms.py:180 +#: preferences/templates/preferences/display_preferences.html:342 msgid "Telephone number" msgstr "Numéro de téléphone" -#: preferences/forms.py:183 -msgid "Profile image" -msgstr "Photo de profil" - -#: preferences/forms.py:184 -#: preferences/templates/preferences/display_preferences.html:343 +#: preferences/forms.py:181 +#: preferences/templates/preferences/display_preferences.html:344 msgid "Usual name" msgstr "Nom d'usage" -#: preferences/forms.py:186 +#: preferences/forms.py:183 msgid "Account used for editing from /admin" msgstr "Compte utilisé pour les modifications depuis /admin" -#: preferences/forms.py:188 preferences/forms.py:333 -#: preferences/templates/preferences/aff_service.html:33 +#: preferences/forms.py:185 preferences/forms.py:332 msgid "Description" msgstr "Description" -#: preferences/forms.py:202 +#: preferences/forms.py:199 msgid "Message for the French welcome email" msgstr "Message pour le mail de bienvenue en français" -#: preferences/forms.py:205 +#: preferences/forms.py:202 msgid "Message for the English welcome email" msgstr "Message pour le mail de bienvenue en anglais" -#: preferences/forms.py:219 +#: preferences/forms.py:218 msgid "Facebook URL" msgstr "URL du compte Facebook" -#: preferences/forms.py:220 +#: preferences/forms.py:219 msgid "Twitter URL" msgstr "URL du compte Twitter" -#: preferences/forms.py:221 -#: preferences/templates/preferences/display_preferences.html:504 +#: preferences/forms.py:220 +#: preferences/templates/preferences/display_preferences.html:505 msgid "Twitter account name" msgstr "Nom du compte Twitter" -#: preferences/forms.py:239 +#: preferences/forms.py:238 msgid "You chose to set vlan but did not set any VLAN." msgstr "" "Vous avez choisi de paramétrer vlan mais vous n'avez indiqué aucun VLAN." -#: preferences/forms.py:240 +#: preferences/forms.py:239 msgid "Please, choose a VLAN." msgstr "Veuillez choisir un VLAN." -#: preferences/forms.py:271 +#: preferences/forms.py:270 msgid "There is already a mandate taking place at the specified start date." msgstr "Il y a déjà un mandat ayant cours à la date de début renseignée." -#: preferences/forms.py:285 +#: preferences/forms.py:284 msgid "There is already a mandate taking place at the specified end date." msgstr "Il y a déjà un madant ayant cours à la date de fin renseignée." -#: preferences/forms.py:299 +#: preferences/forms.py:298 msgid "The specified dates overlap with an existing mandate." msgstr "Les dates renseignées se superposent avec un mandat existant." -#: preferences/forms.py:331 -#: preferences/templates/preferences/aff_service.html:31 -#: preferences/templates/preferences/display_preferences.html:327 +#: preferences/forms.py:330 +#: preferences/templates/preferences/display_preferences.html:328 msgid "Name" msgstr "Nom" -#: preferences/forms.py:332 -#: preferences/templates/preferences/aff_service.html:32 +#: preferences/forms.py:331 +#: preferences/templates/preferences/aff_service.html:55 msgid "URL" msgstr "URL" -#: preferences/forms.py:334 -#: preferences/templates/preferences/aff_service.html:34 +#: preferences/forms.py:333 +#: preferences/templates/preferences/aff_service.html:58 msgid "Image" msgstr "Image" @@ -286,73 +280,73 @@ msgstr "Image" msgid "Current services" msgstr "Services actuels" -#: preferences/forms.py:431 +#: preferences/forms.py:430 msgid "Current email addresses" msgstr "Adresses mail actuelles" -#: preferences/forms.py:466 +#: preferences/forms.py:460 msgid "Current document templates" msgstr "Modèles de document actuels" -#: preferences/forms.py:496 +#: preferences/forms.py:490 msgid "Current attributes" msgstr "Attributs actuels" -#: preferences/models.py:78 +#: preferences/models.py:109 msgid "Users can't select their room" msgstr "Les utilisateurs ne peuvent pas modifier leur chambre" -#: preferences/models.py:82 +#: preferences/models.py:113 msgid "" "Users can only select a room occupied by a user with a disabled connection." msgstr "" "Les utilisateurs peuvent sélectionner la chambre d'un adhérent dont la " "connexion est désactivée." -#: preferences/models.py:85 +#: preferences/models.py:116 msgid "Users can select all rooms" msgstr "Les utilisateurs peuvent choisir toutes les chambres" -#: preferences/models.py:91 +#: preferences/models.py:122 msgid "Users can create a club." msgstr "Les utilisateurs peuvent créer un club." -#: preferences/models.py:94 +#: preferences/models.py:125 msgid "Users can create a member." msgstr "Les utilisateurs peuvent créer un adhérent." -#: preferences/models.py:100 +#: preferences/models.py:131 msgid "Users can edit their shell." msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande." -#: preferences/models.py:103 +#: preferences/models.py:134 msgid "Users can edit their pseudo." msgstr "Les utilisateurs peuvent modifier leur pseudo" -#: preferences/models.py:109 +#: preferences/models.py:140 msgid "Policy on self users room edition" msgstr "Autorisation d'édtion du champ chambre par les utilisateurs" -#: preferences/models.py:112 +#: preferences/models.py:143 msgid "Enable local email accounts for users." msgstr "Activer les comptes mail locaux pour les utilisateurs." -#: preferences/models.py:117 +#: preferences/models.py:148 msgid "Domain to use for local email accounts." msgstr "Domaine à utiliser pour les comptes mail locaux." -#: preferences/models.py:121 +#: preferences/models.py:152 msgid "Maximum number of local email addresses for a standard user." msgstr "" "Nombre maximum d'adresses mail locales autorisé pour un utilisateur standard." -#: preferences/models.py:125 +#: preferences/models.py:156 msgid "Not yet active users will be deleted after this number of days." msgstr "" "Les utilisateurs n'ayant jamais adhéré seront supprimés après ce nombre de " "jours." -#: preferences/models.py:130 +#: preferences/models.py:161 msgid "" "Users with an email address not yet confirmed will be disabled after this " "number of days." @@ -360,11 +354,11 @@ msgstr "" "Les utilisateurs n'ayant pas confirmé leur addresse mail seront désactivés " "après ce nombre de jours" -#: preferences/models.py:134 +#: preferences/models.py:165 msgid "A new user can create their account on Re2o." msgstr "Un nouvel utilisateur peut créer son compte sur Re2o." -#: preferences/models.py:139 +#: preferences/models.py:170 msgid "" "If True, all new created and connected users are active. If False, only when " "a valid registration has been paid." @@ -372,7 +366,7 @@ msgstr "" "Si True, tous les nouveaux utilisations créés et connectés sont actifs. Si " "False, seulement quand une inscription validée a été payée." -#: preferences/models.py:146 +#: preferences/models.py:177 msgid "" "If True, users have the choice to receive an email containing a link to " "reset their password during creation, or to directly set their password in " @@ -383,172 +377,172 @@ msgstr "" "de choisir leur mot de passe immédiatement. Si False, un mail est toujours " "envoyé." -#: preferences/models.py:153 +#: preferences/models.py:184 msgid "If True, archived users are allowed to connect." msgstr "Si True, les utilisateurs archivés sont autorisés à se connecter." -#: preferences/models.py:157 +#: preferences/models.py:188 msgid "Can view the user preferences" msgstr "Peut voir les préférences d'utilisateur" -#: preferences/models.py:158 +#: preferences/models.py:189 msgid "user preferences" msgstr "Préférences d'utilisateur" -#: preferences/models.py:165 +#: preferences/models.py:194 msgid "Email domain must begin with @." msgstr "Un domaine mail doit commencer par @." -#: preferences/models.py:183 +#: preferences/models.py:223 msgid "Automatic configuration by RA" msgstr "Configuration automatique par RA" -#: preferences/models.py:184 +#: preferences/models.py:224 msgid "IP addresses assignment by DHCPv6" msgstr "Attribution d'adresses IP par DHCPv6" -#: preferences/models.py:185 +#: preferences/models.py:225 msgid "Disabled" msgstr "Désactivé" -#: preferences/models.py:194 +#: preferences/models.py:234 msgid "default Time To Live (TTL) for CNAME, A and AAAA records" msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA" -#: preferences/models.py:204 +#: preferences/models.py:244 msgid "Can view the machine preferences" msgstr "Peut voir les préférences de machine" -#: preferences/models.py:205 +#: preferences/models.py:245 msgid "machine preferences" msgstr "Préférences de machine" -#: preferences/models.py:225 preferences/models.py:687 +#: preferences/models.py:278 preferences/models.py:864 msgid "On the IP range's VLAN of the machine" msgstr "Sur le VLAN de la plage d'IP de la machine" -#: preferences/models.py:226 preferences/models.py:688 +#: preferences/models.py:279 preferences/models.py:865 msgid "Preset in \"VLAN for machines accepted by RADIUS\"" msgstr "Prédéfinie dans « VLAN pour les machines acceptées par RADIUS »" -#: preferences/models.py:232 +#: preferences/models.py:285 msgid "Web management, activated in case of automatic provision." msgstr "Gestion web, activée en cas de provision automatique." -#: preferences/models.py:237 +#: preferences/models.py:290 msgid "" "SSL web management, make sure that a certificate is installed on the switch." msgstr "" "Gestion web SSL, vérifiez qu'un certificat est installé sur le commutateur " "réseau." -#: preferences/models.py:243 +#: preferences/models.py:296 msgid "REST management, activated in case of automatic provision." msgstr "Gestion REST, activée en cas de provision automatique." -#: preferences/models.py:250 +#: preferences/models.py:303 msgid "IP range for the management of switches." msgstr "Plage d'IP pour la gestion des commutateurs réseau." -#: preferences/models.py:256 +#: preferences/models.py:309 msgid "Provision of configuration mode for switches." msgstr "Mode de provision de configuration pour les commutateurs réseau." -#: preferences/models.py:259 +#: preferences/models.py:312 msgid "SFTP login for switches." msgstr "Identifiant SFTP pour les commutateurs réseau." -#: preferences/models.py:262 +#: preferences/models.py:315 msgid "SFTP password." msgstr "Mot de passe SFTP." -#: preferences/models.py:367 +#: preferences/models.py:425 msgid "Can view the topology preferences" msgstr "Peut voir les préférences de topologie" -#: preferences/models.py:369 +#: preferences/models.py:427 msgid "topology preferences" msgstr "préférences de topologie" -#: preferences/models.py:382 +#: preferences/models.py:447 msgid "RADIUS key." msgstr "Clé RADIUS." -#: preferences/models.py:384 +#: preferences/models.py:449 msgid "Comment for this key." msgstr "Commentaire pour cette clé." -#: preferences/models.py:387 +#: preferences/models.py:452 msgid "Default key for switches." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:391 +#: preferences/models.py:456 msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:392 preferences/views.py:335 +#: preferences/models.py:457 preferences/views.py:334 msgid "RADIUS key" msgstr "Clé RADIUS" -#: preferences/models.py:393 -#: preferences/templates/preferences/display_preferences.html:223 +#: preferences/models.py:458 +#: preferences/templates/preferences/display_preferences.html:224 msgid "RADIUS keys" msgstr "clés RADIUS" -#: preferences/models.py:400 +#: preferences/models.py:463 msgid "Default RADIUS key for switches already exists." msgstr "Clé par défaut pour les commutateurs réseau." -#: preferences/models.py:403 +#: preferences/models.py:466 msgid "RADIUS key " msgstr "clé RADIUS " -#: preferences/models.py:409 +#: preferences/models.py:479 msgid "Switch login." msgstr "Identifiant du commutateur réseau." -#: preferences/models.py:410 +#: preferences/models.py:480 msgid "Password." msgstr "Mot de passe." -#: preferences/models.py:412 +#: preferences/models.py:482 msgid "Default credentials for switches." msgstr "Identifiants par défaut pour les commutateurs réseau." -#: preferences/models.py:419 +#: preferences/models.py:489 msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:422 preferences/views.py:397 +#: preferences/models.py:492 preferences/views.py:396 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" -#: preferences/models.py:425 +#: preferences/models.py:495 msgid "Switch login " msgstr "Identifiant du commutateur réseau " -#: preferences/models.py:437 +#: preferences/models.py:511 msgid "Delay between the email and the membership's end." msgstr "Délai entre le mail et la fin d'adhésion." -#: preferences/models.py:443 +#: preferences/models.py:517 msgid "Message displayed specifically for this reminder." msgstr "Message affiché spécifiquement pour ce rappel." -#: preferences/models.py:447 +#: preferences/models.py:521 msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:448 preferences/views.py:280 +#: preferences/models.py:522 preferences/views.py:279 msgid "reminder" msgstr "rappel" -#: preferences/models.py:449 +#: preferences/models.py:523 msgid "reminders" msgstr "rappels" -#: preferences/models.py:470 +#: preferences/models.py:562 msgid "" "General message displayed on the French version of the website (e.g. in case " "of maintenance)." @@ -556,7 +550,7 @@ msgstr "" "Message général affiché sur la version française du site (ex : en cas de " "maintenance)." -#: preferences/models.py:478 +#: preferences/models.py:570 msgid "" "General message displayed on the English version of the website (e.g. in " "case of maintenance)." @@ -564,75 +558,75 @@ msgstr "" "Message général affiché sur la version anglaise du site (ex : en cas de " "maintenance)." -#: preferences/models.py:493 +#: preferences/models.py:585 msgid "Can view the general preferences" msgstr "Peut voir les préférences générales" -#: preferences/models.py:494 +#: preferences/models.py:586 msgid "general preferences" msgstr "préférences générales" -#: preferences/models.py:514 +#: preferences/models.py:612 msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:515 preferences/views.py:231 +#: preferences/models.py:613 preferences/views.py:230 msgid "service" msgstr "service" -#: preferences/models.py:516 +#: preferences/models.py:614 msgid "services" msgstr "services" -#: preferences/models.py:526 +#: preferences/models.py:629 msgid "Contact email address." msgstr "Adresse mail de contact." -#: preferences/models.py:532 +#: preferences/models.py:635 msgid "Description of the associated email address." msgstr "Description de l'adresse mail associée." -#: preferences/models.py:542 +#: preferences/models.py:645 msgid "Can view a contact email address object" msgstr "Peut voir un objet adresse mail de contact" -#: preferences/models.py:544 +#: preferences/models.py:647 msgid "contact email address" msgstr "adresse mail de contact" -#: preferences/models.py:545 +#: preferences/models.py:648 msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:553 preferences/views.py:635 +#: preferences/models.py:665 preferences/views.py:628 msgid "mandate" msgstr "mandat" -#: preferences/models.py:554 +#: preferences/models.py:666 msgid "mandates" msgstr "mandats" -#: preferences/models.py:555 +#: preferences/models.py:667 msgid "Can view a mandate object" msgstr "Peut voir un objet mandat" -#: preferences/models.py:562 +#: preferences/models.py:674 msgid "president of the association" msgstr "président de l'association" -#: preferences/models.py:563 +#: preferences/models.py:675 msgid "Displayed on subscription vouchers." msgstr "Affiché sur les reçus de cotisation." -#: preferences/models.py:565 +#: preferences/models.py:677 msgid "start date" msgstr "date de début" -#: preferences/models.py:566 +#: preferences/models.py:678 msgid "end date" msgstr "date de fin" -#: preferences/models.py:580 +#: preferences/models.py:699 msgid "" "No mandates have been created. Please go to the preferences page to create " "one." @@ -640,140 +634,140 @@ msgstr "" "Aucun mandat n'a été créé. Veuillez vous rendre sur la page de préférences " "pour en créer un." -#: preferences/models.py:596 +#: preferences/models.py:729 msgid "Networking organisation school Something" msgstr "Association de réseau de l'école Machin" -#: preferences/models.py:599 +#: preferences/models.py:732 msgid "Threadneedle Street" msgstr "1 rue de la Vrillière" -#: preferences/models.py:600 +#: preferences/models.py:733 msgid "London EC2R 8AH" msgstr "75001 Paris" -#: preferences/models.py:603 +#: preferences/models.py:736 msgid "Organisation" msgstr "Association" -#: preferences/models.py:610 +#: preferences/models.py:743 msgid "Can view the organisation preferences" msgstr "Peut voir les préférences d'association" -#: preferences/models.py:611 +#: preferences/models.py:744 msgid "organisation preferences" msgstr "préférences d'association" -#: preferences/models.py:629 +#: preferences/models.py:769 msgid "Can view the homepage preferences" msgstr "Peut voir les préférences de page d'accueil" -#: preferences/models.py:630 +#: preferences/models.py:770 msgid "homepage preferences" msgstr "Préférences de page d'accueil" -#: preferences/models.py:644 +#: preferences/models.py:789 msgid "Welcome email in French." msgstr "Mail de bienvenue en français." -#: preferences/models.py:647 +#: preferences/models.py:792 msgid "Welcome email in English." msgstr "Mail de bienvenue en anglais." -#: preferences/models.py:652 +#: preferences/models.py:797 msgid "Can view the email message preferences" msgstr "Peut voir les préférences de message pour les mails" -#: preferences/models.py:654 +#: preferences/models.py:799 msgid "email message preferences" msgstr "préférences de messages pour les mails" -#: preferences/models.py:659 +#: preferences/models.py:812 msgid "RADIUS attribute" msgstr "attribut RADIUS" -#: preferences/models.py:660 +#: preferences/models.py:813 msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:664 preferences/views.py:588 +#: preferences/models.py:817 preferences/views.py:581 msgid "attribute" msgstr "attribut" -#: preferences/models.py:665 +#: preferences/models.py:818 msgid "See https://freeradius.org/rfc/attributes.html." msgstr "Voir https://freeradius.org/rfc/attributes.html." -#: preferences/models.py:667 +#: preferences/models.py:820 msgid "value" msgstr "valeur" -#: preferences/models.py:669 +#: preferences/models.py:822 msgid "comment" msgstr "commentaire" -#: preferences/models.py:670 +#: preferences/models.py:823 msgid "Use this field to document this attribute." msgstr "Utilisez ce champ pour documenter cet attribut." -#: preferences/models.py:681 +#: preferences/models.py:858 msgid "RADIUS policy" msgstr "politique de RADIUS" -#: preferences/models.py:682 -#: preferences/templates/preferences/display_preferences.html:301 +#: preferences/models.py:859 +#: preferences/templates/preferences/display_preferences.html:302 msgid "RADIUS policies" msgstr "politiques de RADIUS" -#: preferences/models.py:693 +#: preferences/models.py:870 msgid "Reject the machine" msgstr "Rejeter la machine" -#: preferences/models.py:694 +#: preferences/models.py:871 msgid "Place the machine on the VLAN" msgstr "Placer la machine sur le VLAN" -#: preferences/models.py:703 +#: preferences/models.py:880 msgid "policy for unknown machines" msgstr "politique pour les machines inconnues" -#: preferences/models.py:711 +#: preferences/models.py:888 msgid "unknown machines VLAN" msgstr "VLAN pour les machines inconnues" -#: preferences/models.py:712 +#: preferences/models.py:889 msgid "VLAN for unknown machines if not rejected." msgstr "VLAN pour les machines inconnues si non rejeté." -#: preferences/models.py:718 +#: preferences/models.py:895 msgid "unknown machines attributes" msgstr "attributs pour les machines inconnues" -#: preferences/models.py:719 +#: preferences/models.py:896 msgid "Answer attributes for unknown machines." msgstr "Attributs de réponse pour les machines inconnues." -#: preferences/models.py:725 +#: preferences/models.py:902 msgid "policy for unknown ports" msgstr "politique pour les ports inconnus" -#: preferences/models.py:733 +#: preferences/models.py:910 msgid "unknown ports VLAN" msgstr "VLAN pour les ports inconnus" -#: preferences/models.py:734 +#: preferences/models.py:911 msgid "VLAN for unknown ports if not rejected." msgstr "VLAN pour les ports inconnus si non rejeté." -#: preferences/models.py:740 +#: preferences/models.py:917 msgid "unknown ports attributes" msgstr "attributs pour les ports inconnus" -#: preferences/models.py:741 +#: preferences/models.py:918 msgid "Answer attributes for unknown ports." msgstr "Attributs de réponse pour les ports inconnus." -#: preferences/models.py:748 +#: preferences/models.py:925 msgid "" "Policy for machines connecting from unregistered rooms (relevant on ports " "with STRICT RADIUS mode)" @@ -781,87 +775,87 @@ msgstr "" "Politique pour les machines se connectant depuis des chambre non " "enregistrées (pertinent pour les ports avec le mode de RADIUS STRICT)" -#: preferences/models.py:758 +#: preferences/models.py:935 msgid "unknown rooms VLAN" msgstr "VLAN pour les chambres inconnues" -#: preferences/models.py:759 +#: preferences/models.py:936 msgid "VLAN for unknown rooms if not rejected." msgstr "VLAN pour les chambres inconnues si non rejeté." -#: preferences/models.py:765 +#: preferences/models.py:942 msgid "unknown rooms attributes" msgstr "attributs pour les chambres inconnues" -#: preferences/models.py:766 +#: preferences/models.py:943 msgid "Answer attributes for unknown rooms." msgstr "Attributs de réponse pour les chambres inconnues." -#: preferences/models.py:772 +#: preferences/models.py:949 msgid "policy for non members" msgstr "politique pour les non adhérents" -#: preferences/models.py:780 +#: preferences/models.py:957 msgid "non members VLAN" msgstr "VLAN pour les non adhérents" -#: preferences/models.py:781 +#: preferences/models.py:958 msgid "VLAN for non members if not rejected." msgstr "VLAN pour les non adhérents si non rejeté." -#: preferences/models.py:787 +#: preferences/models.py:964 msgid "non members attributes" msgstr "attributs pour les non adhérents" -#: preferences/models.py:788 +#: preferences/models.py:965 msgid "Answer attributes for non members." msgstr "Attributs de réponse pour les non adhérents." -#: preferences/models.py:794 +#: preferences/models.py:971 msgid "policy for banned users" msgstr "politique pour les utilisateurs bannis" -#: preferences/models.py:802 +#: preferences/models.py:979 msgid "banned users VLAN" msgstr "VLAN pour les utilisateurs bannis" -#: preferences/models.py:803 +#: preferences/models.py:980 msgid "VLAN for banned users if not rejected." msgstr "VLAN pour les utilisateurs bannis si non rejeté." -#: preferences/models.py:809 +#: preferences/models.py:986 msgid "banned users attributes" msgstr "attributs pour les utilisateurs bannis" -#: preferences/models.py:810 +#: preferences/models.py:987 msgid "Answer attributes for banned users." msgstr "Attributs de réponse pour les utilisateurs bannis." -#: preferences/models.py:823 +#: preferences/models.py:1000 msgid "accepted users attributes" msgstr "attributs pour les utilisateurs acceptés" -#: preferences/models.py:824 +#: preferences/models.py:1001 msgid "Answer attributes for accepted users." msgstr "Attributs de réponse pour les utilisateurs acceptés." -#: preferences/models.py:851 +#: preferences/models.py:1037 msgid "subscription preferences" msgstr "préférences de cotisation" -#: preferences/models.py:855 +#: preferences/models.py:1041 msgid "template for invoices" msgstr "modèle pour les factures" -#: preferences/models.py:862 +#: preferences/models.py:1048 msgid "template for subscription vouchers" msgstr "modèle pour les reçus de cotisation" -#: preferences/models.py:868 +#: preferences/models.py:1054 msgid "send voucher by email when the invoice is controlled" msgstr "envoyer le reçu par mail quand la facture est contrôlée" -#: preferences/models.py:870 +#: preferences/models.py:1056 msgid "" "Be careful, if no mandate is defined on the preferences page, errors will be " "triggered when generating vouchers." @@ -869,19 +863,19 @@ msgstr "" "Faites attention, si aucun mandat n'est défini sur la page de préférences, " "des erreurs seront déclenchées en générant des reçus." -#: preferences/models.py:882 +#: preferences/models.py:1072 msgid "template" msgstr "modèle" -#: preferences/models.py:883 +#: preferences/models.py:1073 msgid "name" msgstr "nom" -#: preferences/models.py:886 +#: preferences/models.py:1076 msgid "document template" msgstr "modèle de document" -#: preferences/models.py:887 +#: preferences/models.py:1077 msgid "document templates" msgstr "modèles de document" @@ -894,7 +888,7 @@ msgid "File" msgstr "Fichier" #: preferences/templates/preferences/aff_mailcontact.html:31 -#: preferences/templates/preferences/display_preferences.html:333 +#: preferences/templates/preferences/display_preferences.html:334 msgid "Address" msgstr "Adresse" @@ -1063,340 +1057,341 @@ msgid "Confirm" msgstr "Confirmer" #: preferences/templates/preferences/display_preferences.html:31 +#: preferences/templates/preferences/display_preferences.html:34 #: preferences/templates/preferences/edit_preferences.html:30 #: preferences/templates/preferences/preferences.html:30 msgid "Preferences" msgstr "Préférences" -#: preferences/templates/preferences/display_preferences.html:39 +#: preferences/templates/preferences/display_preferences.html:40 msgid "General preferences" msgstr "Préférences générales" -#: preferences/templates/preferences/display_preferences.html:46 -#: preferences/templates/preferences/display_preferences.html:108 -#: preferences/templates/preferences/display_preferences.html:189 -#: preferences/templates/preferences/display_preferences.html:242 -#: preferences/templates/preferences/display_preferences.html:304 -#: preferences/templates/preferences/display_preferences.html:322 -#: preferences/templates/preferences/display_preferences.html:419 -#: preferences/templates/preferences/display_preferences.html:497 +#: preferences/templates/preferences/display_preferences.html:47 +#: preferences/templates/preferences/display_preferences.html:109 +#: preferences/templates/preferences/display_preferences.html:190 +#: preferences/templates/preferences/display_preferences.html:243 +#: preferences/templates/preferences/display_preferences.html:305 +#: preferences/templates/preferences/display_preferences.html:323 +#: preferences/templates/preferences/display_preferences.html:420 +#: preferences/templates/preferences/display_preferences.html:498 #: preferences/templates/preferences/edit_preferences.html:46 -#: preferences/views.py:216 preferences/views.py:265 preferences/views.py:311 -#: preferences/views.py:368 preferences/views.py:432 preferences/views.py:497 -#: preferences/views.py:573 preferences/views.py:620 +#: preferences/views.py:215 preferences/views.py:264 preferences/views.py:310 +#: preferences/views.py:367 preferences/views.py:431 preferences/views.py:492 +#: preferences/views.py:566 preferences/views.py:613 msgid "Edit" msgstr "Modifier" -#: preferences/templates/preferences/display_preferences.html:60 +#: preferences/templates/preferences/display_preferences.html:61 msgid "Number of items per page (standard size)" msgstr "Nombre d'éléments par page (taille standard)" -#: preferences/templates/preferences/display_preferences.html:64 +#: preferences/templates/preferences/display_preferences.html:65 msgid "Number of items per page (large size)" msgstr "Nombre d'éléments par page (taille importante)" -#: preferences/templates/preferences/display_preferences.html:70 +#: preferences/templates/preferences/display_preferences.html:71 msgid "General message displayed on the website" msgstr "Message général affiché sur le site" -#: preferences/templates/preferences/display_preferences.html:72 +#: preferences/templates/preferences/display_preferences.html:73 msgid "Main site URL" msgstr "URL du site principal" -#: preferences/templates/preferences/display_preferences.html:90 +#: preferences/templates/preferences/display_preferences.html:91 msgid "Maximum number of email aliases allowed" msgstr "Nombre maximum d'alias mail autorisé pour un utilisateur standard" -#: preferences/templates/preferences/display_preferences.html:100 +#: preferences/templates/preferences/display_preferences.html:101 msgid "User preferences" msgstr "Préférences d'utilisateur" -#: preferences/templates/preferences/display_preferences.html:112 +#: preferences/templates/preferences/display_preferences.html:113 msgid "Accounts creation and self-register" msgstr "Creation des comptes et autoenregistrement" -#: preferences/templates/preferences/display_preferences.html:115 +#: preferences/templates/preferences/display_preferences.html:116 msgid "Creation of members by everyone" msgstr "Création d'adhérents par tous" -#: preferences/templates/preferences/display_preferences.html:117 +#: preferences/templates/preferences/display_preferences.html:118 msgid "Creation of clubs by everyone" msgstr "Création de clubs par tous" -#: preferences/templates/preferences/display_preferences.html:123 +#: preferences/templates/preferences/display_preferences.html:124 msgid "Delete not yet active users after" msgstr "Suppression des utilisateurs n'ayant jamais adhéré après" -#: preferences/templates/preferences/display_preferences.html:124 +#: preferences/templates/preferences/display_preferences.html:125 #, python-format msgid "%(delete_notyetactive)s days" msgstr "%(delete_notyetactive)s jours" -#: preferences/templates/preferences/display_preferences.html:127 +#: preferences/templates/preferences/display_preferences.html:128 msgid "All users are active by default" msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/templates/preferences/display_preferences.html:129 +#: preferences/templates/preferences/display_preferences.html:130 msgid "Allow archived users to log in" msgstr "Autoriser les utilisateurs archivés à se connecter" -#: preferences/templates/preferences/display_preferences.html:133 +#: preferences/templates/preferences/display_preferences.html:134 msgid "Allow directly entering a password during account creation" msgstr "" "Permettre le choix d'un mot de passe directement lors de la création du " "compte" -#: preferences/templates/preferences/display_preferences.html:135 +#: preferences/templates/preferences/display_preferences.html:136 msgid "Delay before disabling accounts without a verified email" msgstr "Délai avant la désactivation des comptes sans adresse mail confirmé" -#: preferences/templates/preferences/display_preferences.html:136 +#: preferences/templates/preferences/display_preferences.html:137 #, python-format msgid "%(disable_emailnotyetconfirmed)s days" msgstr "%(disable_emailnotyetconfirmed)s jours" -#: preferences/templates/preferences/display_preferences.html:140 +#: preferences/templates/preferences/display_preferences.html:141 msgid "Users general permissions" msgstr "Permissions générales des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:143 +#: preferences/templates/preferences/display_preferences.html:144 msgid "Default shell for users" msgstr "Interface en ligne de commande par défaut pour les utilisateurs" -#: preferences/templates/preferences/display_preferences.html:145 +#: preferences/templates/preferences/display_preferences.html:146 msgid "Users can edit their shell" msgstr "Les utilisateurs peuvent modifier leur interface en ligne de commande" -#: preferences/templates/preferences/display_preferences.html:151 +#: preferences/templates/preferences/display_preferences.html:152 msgid "GPG fingerprint field" msgstr "Champ empreinte GPG" -#: preferences/templates/preferences/display_preferences.html:155 +#: preferences/templates/preferences/display_preferences.html:156 msgid "Policy for self-user room change" msgstr "Autorisations pour le changement de chambre des utilisateurs" -#: preferences/templates/preferences/display_preferences.html:157 +#: preferences/templates/preferences/display_preferences.html:158 msgid "Users can edit their pseudo" msgstr "Les utilisateurs peuvent modifier leur pseudo" -#: preferences/templates/preferences/display_preferences.html:161 +#: preferences/templates/preferences/display_preferences.html:162 msgid "Local email accounts settings" msgstr "Réglages des comptes email locaux" -#: preferences/templates/preferences/display_preferences.html:164 +#: preferences/templates/preferences/display_preferences.html:165 msgid "Local email accounts state" msgstr "Etat des comptes email locaux" -#: preferences/templates/preferences/display_preferences.html:170 +#: preferences/templates/preferences/display_preferences.html:171 msgid "Maximum of local email address" msgstr "Maximum de mail local autorisés" -#: preferences/templates/preferences/display_preferences.html:181 +#: preferences/templates/preferences/display_preferences.html:182 msgid "Machine preferences" msgstr "Préférences de machine" -#: preferences/templates/preferences/display_preferences.html:194 +#: preferences/templates/preferences/display_preferences.html:195 msgid "Password per machine" msgstr "Mot de passe par machine" -#: preferences/templates/preferences/display_preferences.html:202 +#: preferences/templates/preferences/display_preferences.html:203 msgid "Default Time To Live (TTL) for CNAME, A and AAAA records." msgstr "" "Temps de vie (TTL) par défault pour des enregistrements CNAME, A et AAAA." -#: preferences/templates/preferences/display_preferences.html:206 +#: preferences/templates/preferences/display_preferences.html:207 msgid "IPv6 support" msgstr "Support de l'IPv6" -#: preferences/templates/preferences/display_preferences.html:208 +#: preferences/templates/preferences/display_preferences.html:209 msgid "Creation of machines" msgstr "Création de machines" -#: preferences/templates/preferences/display_preferences.html:218 +#: preferences/templates/preferences/display_preferences.html:219 msgid "Topology preferences" msgstr "Préférences de topologie" -#: preferences/templates/preferences/display_preferences.html:225 +#: preferences/templates/preferences/display_preferences.html:226 msgid "Add a RADIUS key" msgstr "Ajouter une clé RADIUS" -#: preferences/templates/preferences/display_preferences.html:235 +#: preferences/templates/preferences/display_preferences.html:236 msgid "Configuration of switches" msgstr "Configuration de commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:248 +#: preferences/templates/preferences/display_preferences.html:249 msgid "Web management, activated in case of automatic provision" msgstr "Gestion web, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:250 +#: preferences/templates/preferences/display_preferences.html:251 msgid "REST management, activated in case of automatic provision" msgstr "Gestion REST, activée en cas de provision automatique" -#: preferences/templates/preferences/display_preferences.html:257 +#: preferences/templates/preferences/display_preferences.html:258 msgid "Provision of configuration for switches" msgstr "Provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:260 +#: preferences/templates/preferences/display_preferences.html:261 msgid "Switches with automatic provision" msgstr "Commutateurs réseau avec provision automatique" -#: preferences/templates/preferences/display_preferences.html:261 -#: preferences/templates/preferences/display_preferences.html:265 -#: preferences/templates/preferences/display_preferences.html:269 -#: preferences/templates/preferences/display_preferences.html:277 -#: preferences/templates/preferences/display_preferences.html:281 -#: preferences/templates/preferences/display_preferences.html:291 +#: preferences/templates/preferences/display_preferences.html:262 +#: preferences/templates/preferences/display_preferences.html:266 +#: preferences/templates/preferences/display_preferences.html:270 +#: preferences/templates/preferences/display_preferences.html:278 +#: preferences/templates/preferences/display_preferences.html:282 +#: preferences/templates/preferences/display_preferences.html:292 msgid "OK" msgstr "OK" -#: preferences/templates/preferences/display_preferences.html:261 -#: preferences/templates/preferences/display_preferences.html:265 -#: preferences/templates/preferences/display_preferences.html:269 -#: preferences/templates/preferences/display_preferences.html:291 +#: preferences/templates/preferences/display_preferences.html:262 +#: preferences/templates/preferences/display_preferences.html:266 +#: preferences/templates/preferences/display_preferences.html:270 +#: preferences/templates/preferences/display_preferences.html:292 msgid "Missing" msgstr "Manquant" -#: preferences/templates/preferences/display_preferences.html:264 +#: preferences/templates/preferences/display_preferences.html:265 msgid "IP range for the management of switches" msgstr "Plage d'IP pour la gestion des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:268 +#: preferences/templates/preferences/display_preferences.html:269 msgid "Server for the configuration of switches" msgstr "Serveur pour la configuration des commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:272 +#: preferences/templates/preferences/display_preferences.html:273 msgid "Provision of configuration mode for switches" msgstr "Mode de provision de configuration pour les commutateurs réseau" -#: preferences/templates/preferences/display_preferences.html:276 +#: preferences/templates/preferences/display_preferences.html:277 msgid "TFTP mode" msgstr "Mode TFTP" -#: preferences/templates/preferences/display_preferences.html:280 +#: preferences/templates/preferences/display_preferences.html:281 msgid "SFTP mode" msgstr "Mode SFTP" -#: preferences/templates/preferences/display_preferences.html:281 +#: preferences/templates/preferences/display_preferences.html:282 msgid "Missing credentials" msgstr "Identifiants manquants" -#: preferences/templates/preferences/display_preferences.html:285 +#: preferences/templates/preferences/display_preferences.html:286 msgid "Switch management credentials" msgstr "Identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:287 +#: preferences/templates/preferences/display_preferences.html:288 msgid "Add switch management credentials" msgstr "Ajouter des identifiants de gestion de commutateur réseau" -#: preferences/templates/preferences/display_preferences.html:298 +#: preferences/templates/preferences/display_preferences.html:299 msgid "RADIUS preferences" msgstr "Préférences RADIUS" -#: preferences/templates/preferences/display_preferences.html:307 +#: preferences/templates/preferences/display_preferences.html:308 msgid "Current RADIUS attributes" msgstr "Attributs RADIUS actuels" -#: preferences/templates/preferences/display_preferences.html:308 +#: preferences/templates/preferences/display_preferences.html:309 msgid "Add an attribute" msgstr "Ajouter un attribut" -#: preferences/templates/preferences/display_preferences.html:316 +#: preferences/templates/preferences/display_preferences.html:317 msgid "Information about the organisation" msgstr "Informations sur l'association" -#: preferences/templates/preferences/display_preferences.html:347 +#: preferences/templates/preferences/display_preferences.html:348 msgid "User object of the organisation" msgstr "Objet utilisateur de l'association" -#: preferences/templates/preferences/display_preferences.html:349 +#: preferences/templates/preferences/display_preferences.html:350 msgid "Description of the organisation" msgstr "Description de l'association" -#: preferences/templates/preferences/display_preferences.html:353 +#: preferences/templates/preferences/display_preferences.html:354 msgid "Mandates" msgstr "Mandats" -#: preferences/templates/preferences/display_preferences.html:356 +#: preferences/templates/preferences/display_preferences.html:357 msgid "Add a mandate" msgstr "Ajouter un mandat" -#: preferences/templates/preferences/display_preferences.html:365 +#: preferences/templates/preferences/display_preferences.html:366 msgid "Document templates" msgstr "Modèles de document" -#: preferences/templates/preferences/display_preferences.html:371 +#: preferences/templates/preferences/display_preferences.html:372 msgid "Add a document template" msgstr "Ajouter un modèle de document" -#: preferences/templates/preferences/display_preferences.html:375 +#: preferences/templates/preferences/display_preferences.html:376 msgid "Delete one or several document templates" msgstr " Supprimer un ou plusieurs modèles de document" -#: preferences/templates/preferences/display_preferences.html:384 +#: preferences/templates/preferences/display_preferences.html:385 msgid "Subscription preferences" msgstr "Préférences de cotisation" -#: preferences/templates/preferences/display_preferences.html:393 +#: preferences/templates/preferences/display_preferences.html:394 msgid "Send voucher by email" msgstr "Envoyer le reçu par mail" -#: preferences/templates/preferences/display_preferences.html:397 +#: preferences/templates/preferences/display_preferences.html:398 msgid "Invoices' template" msgstr "Modèle des factures" -#: preferences/templates/preferences/display_preferences.html:401 +#: preferences/templates/preferences/display_preferences.html:402 msgid "Vouchers' template" msgstr "Modèle des reçus" -#: preferences/templates/preferences/display_preferences.html:412 +#: preferences/templates/preferences/display_preferences.html:413 msgid "Message for emails" msgstr "Message pour les mails" -#: preferences/templates/preferences/display_preferences.html:425 +#: preferences/templates/preferences/display_preferences.html:426 msgid "Welcome email (in French)" msgstr "Mail de bienvenue (en français)" -#: preferences/templates/preferences/display_preferences.html:429 +#: preferences/templates/preferences/display_preferences.html:430 msgid "Welcome email (in English)" msgstr "Mail de bienvenue (en anglais)" -#: preferences/templates/preferences/display_preferences.html:439 +#: preferences/templates/preferences/display_preferences.html:440 msgid "Preferences for the membership's end email" msgstr "Préférences pour le mail de fin d'adhésion" -#: preferences/templates/preferences/display_preferences.html:445 +#: preferences/templates/preferences/display_preferences.html:446 msgid "Add a reminder" msgstr "Ajouter un rappel" -#: preferences/templates/preferences/display_preferences.html:456 +#: preferences/templates/preferences/display_preferences.html:457 msgid "List of services and homepage preferences" msgstr "Liste des services et préférences de page d'accueil" -#: preferences/templates/preferences/display_preferences.html:462 +#: preferences/templates/preferences/display_preferences.html:463 msgid "Add a service" msgstr "Ajouter un service" -#: preferences/templates/preferences/display_preferences.html:473 +#: preferences/templates/preferences/display_preferences.html:474 msgid "List of contact email addresses" msgstr "Liste des adresses mail de contact" -#: preferences/templates/preferences/display_preferences.html:479 +#: preferences/templates/preferences/display_preferences.html:480 msgid "Add an address" msgstr "Ajouter une adresse" -#: preferences/templates/preferences/display_preferences.html:481 +#: preferences/templates/preferences/display_preferences.html:482 msgid "Delete one or several addresses" msgstr "Supprimer une ou plusieurs adresses" -#: preferences/templates/preferences/display_preferences.html:490 +#: preferences/templates/preferences/display_preferences.html:491 msgid "Social networks" msgstr "Réseaux sociaux" -#: preferences/templates/preferences/display_preferences.html:502 +#: preferences/templates/preferences/display_preferences.html:503 msgid "Twitter account URL" msgstr "URL du compte Twitter" -#: preferences/templates/preferences/display_preferences.html:508 +#: preferences/templates/preferences/display_preferences.html:509 msgid "Facebook account URL" msgstr "URL du compte Facebook" @@ -1412,67 +1407,67 @@ msgstr "Objet inconnu." msgid "The preferences were edited." msgstr "Les préférences ont été modifiées." -#: preferences/views.py:195 +#: preferences/views.py:194 msgid "The service was added." msgstr "Le service a été ajouté." -#: preferences/views.py:198 preferences/views.py:247 preferences/views.py:296 -#: preferences/views.py:351 preferences/views.py:414 preferences/views.py:472 -#: preferences/views.py:555 preferences/views.py:604 +#: preferences/views.py:197 preferences/views.py:246 preferences/views.py:295 +#: preferences/views.py:350 preferences/views.py:413 preferences/views.py:469 +#: preferences/views.py:548 preferences/views.py:597 msgid "Add" msgstr "Ajouter" -#: preferences/views.py:213 +#: preferences/views.py:212 msgid "The service was edited." msgstr "Le service a été modifié." -#: preferences/views.py:228 +#: preferences/views.py:227 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: preferences/views.py:244 +#: preferences/views.py:243 msgid "The reminder was added." msgstr "Le rappel a été ajouté." -#: preferences/views.py:262 +#: preferences/views.py:261 msgid "The reminder was edited." msgstr "Le rappel a été modifié." -#: preferences/views.py:277 +#: preferences/views.py:276 msgid "The reminder was deleted." msgstr "Le rappel a été supprimé." -#: preferences/views.py:293 +#: preferences/views.py:292 msgid "The RADIUS key was added." msgstr "La clé RADIUS a été ajoutée." -#: preferences/views.py:308 +#: preferences/views.py:307 msgid "The RADIUS key was edited." msgstr "La clé RADIUS a été modifiée." -#: preferences/views.py:324 +#: preferences/views.py:323 msgid "The RADIUS key was deleted." msgstr "La clé RADIUS a été supprimée." -#: preferences/views.py:329 +#: preferences/views.py:328 msgid "The RADIUS key is assigned to at least one switch, you can't delete it." msgstr "" "La clé RADIUS est assignée a au moins un commutateur réseau, vous ne pouvez " "pas la supprimer." -#: preferences/views.py:348 +#: preferences/views.py:347 msgid "The switch management credentials were added." msgstr "Les identifiants de gestion de commutateur réseay ont été ajoutés." -#: preferences/views.py:365 +#: preferences/views.py:364 msgid "The switch management credentials were edited." msgstr "Les identifiants de gestion de commutateur réseau ont été modifiés." -#: preferences/views.py:382 +#: preferences/views.py:381 msgid "The switch management credentials were deleted." msgstr "Les identifiants de gestion de commutateur réseau ont été supprimés." -#: preferences/views.py:388 +#: preferences/views.py:387 msgid "" "The switch management credentials are assigned to at least one switch, you " "can't delete them." @@ -1480,44 +1475,44 @@ msgstr "" "Les identifiants de gestion de commutateur réseau sont assignés à au moins " "un commutateur réseau , vous ne pouvez pas les supprimer." -#: preferences/views.py:411 +#: preferences/views.py:410 msgid "The contact email address was created." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:429 +#: preferences/views.py:428 msgid "The contact email address was edited." msgstr "L'adresse mail de contact a été modifiée." -#: preferences/views.py:447 +#: preferences/views.py:446 msgid "The contact email adress was deleted." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:450 preferences/views.py:537 +#: preferences/views.py:449 preferences/views.py:530 msgid "Delete" msgstr "Supprimer" -#: preferences/views.py:467 +#: preferences/views.py:464 msgid "The document template was created." msgstr "Le modèle de document a été créé." -#: preferences/views.py:473 +#: preferences/views.py:470 msgid "New document template" msgstr "Nouveau modèle de document" -#: preferences/views.py:492 +#: preferences/views.py:487 msgid "The document template was edited." msgstr "Le modèle de document a été édité." -#: preferences/views.py:498 +#: preferences/views.py:493 msgid "Edit document template" msgstr "Modifier le modèle de document" -#: preferences/views.py:521 +#: preferences/views.py:514 #, python-format msgid "The document template %(document_template)s was deleted." msgstr "Le modèle de document %(document_template)s a été supprimé." -#: preferences/views.py:528 +#: preferences/views.py:521 #, python-format msgid "" "The document template %(document_template)s can't be deleted because it is " @@ -1526,34 +1521,37 @@ msgstr "" "Le modèle de document %(document_template)s ne peut pas être supprimé car il " "est actuellement utilisé." -#: preferences/views.py:538 +#: preferences/views.py:531 msgid "Delete document template" msgstr "Supprimer le modèle de document" -#: preferences/views.py:552 +#: preferences/views.py:545 msgid "The attribute was added." msgstr "L'attribut a été ajouté." -#: preferences/views.py:570 +#: preferences/views.py:563 msgid "The attribute was edited." msgstr "L'attribut a été modifié." -#: preferences/views.py:585 +#: preferences/views.py:578 msgid "The attribute was deleted." msgstr "L'attribut a été supprimé." -#: preferences/views.py:601 +#: preferences/views.py:594 msgid "The mandate was added." msgstr "Le mandat a été ajouté." -#: preferences/views.py:617 +#: preferences/views.py:610 msgid "The mandate was edited." msgstr "Le mandat a été modifié." -#: preferences/views.py:632 +#: preferences/views.py:625 msgid "The mandate was deleted." msgstr "Le mandat été supprimé." +#~ msgid "Profile image" +#~ msgstr "Photo de profil" + #~ msgid "Users can edit their room." #~ msgstr "Les utilisateurs peuvent modifier leur chambre." diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 3cdaec99..80596fc8 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" @@ -44,32 +44,32 @@ msgstr "Vous devez être membre de l'un de ces groupes : %s." msgid "No group has the %s permission(s)!" msgstr "Aucun groupe ne possède la ou les permissions %s !" -#: re2o/acl.py:166 +#: re2o/acl.py:194 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: re2o/acl.py:196 re2o/acl.py:267 +#: re2o/acl.py:250 re2o/acl.py:322 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." -#: re2o/acl.py:317 +#: re2o/acl.py:372 msgid "You don't have the right to edit the history." msgstr "Vous n'avez pas le droit de modifier l'historique." -#: re2o/base.py:76 +#: re2o/base.py:74 msgid "This domain is already taken." msgstr "Ce domaine est déjà pris." -#: re2o/base.py:78 +#: re2o/base.py:76 msgid "SMTP unreachable." msgstr "SMTP injoignable." -#: re2o/base.py:99 +#: re2o/base.py:97 #, python-brace-format msgid "Format: {main} {more}" msgstr "Format : {main} {more}" -#: re2o/base.py:101 +#: re2o/base.py:99 #, python-brace-format msgid "Format: {main}" msgstr "Format : {main}" @@ -79,40 +79,40 @@ msgstr "Format : {main}" msgid "Failed to send email: %(error)s." msgstr "Échec de l'envoi du mail : %(error)s." -#: re2o/mixins.py:110 +#: re2o/mixins.py:121 #, python-format msgid "You don't have the right to create a %s object." msgstr "Vous n'avez pas le droit de créer un objet %s." -#: re2o/mixins.py:126 +#: re2o/mixins.py:142 #, python-format msgid "You don't have the right to edit a %s object." msgstr "Vous n'avez pas le droit de modifier un objet %s." -#: re2o/mixins.py:142 +#: re2o/mixins.py:163 #, python-format msgid "You don't have the right to delete a %s object." msgstr "Vous n'avez pas le droit de supprimer un objet %s." -#: re2o/mixins.py:158 +#: re2o/mixins.py:184 #, python-format msgid "You don't have the right to view every %s object." msgstr "Vous n'avez pas le droit de voir tous les objets %s." -#: re2o/mixins.py:174 +#: re2o/mixins.py:205 #, python-format msgid "You don't have the right to view a %s object." msgstr "Vous n'avez pas le droit de voir un objet %s." -#: re2o/settings.py:150 +#: re2o/settings.py:154 msgid "English" msgstr "Anglais" -#: re2o/settings.py:150 +#: re2o/settings.py:154 msgid "French" msgstr "Français" -#: re2o/templates/re2o/about.html:29 re2o/templates/re2o/about.html:54 +#: re2o/templates/re2o/about.html:29 re2o/templates/re2o/about.html:56 msgid "About Re2o" msgstr "À propos de Re2o" @@ -140,15 +140,15 @@ msgstr "Gestionnaire de publication" msgid "President of " msgstr "Président de " -#: re2o/templates/re2o/about.html:46 +#: re2o/templates/re2o/about.html:47 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: re2o/templates/re2o/about.html:50 +#: re2o/templates/re2o/about.html:52 msgid "Additional information" msgstr "Informations supplémentaires" -#: re2o/templates/re2o/about.html:55 +#: re2o/templates/re2o/about.html:57 msgid "" "Re2o is an administration tool initiated by Rezo Metz and a few members of other Remote URL: %(git_info_remote)s" msgstr "URL distante : %(git_info_remote)s" -#: re2o/templates/re2o/about.html:88 +#: re2o/templates/re2o/about.html:90 #, python-format msgid "Branch: %(git_info_branch)s" msgstr "Branche : %(git_info_branch)s" -#: re2o/templates/re2o/about.html:91 +#: re2o/templates/re2o/about.html:93 #, python-format msgid "Commit: %(git_info_commit)s" msgstr "Commit : %(git_info_commit)s" -#: re2o/templates/re2o/about.html:94 +#: re2o/templates/re2o/about.html:96 #, python-format msgid "Commit date: %(git_info_commit_date)s" msgstr "Date du commit : %(git_info_commit_date)s" -#: re2o/templates/re2o/about.html:99 +#: re2o/templates/re2o/about.html:101 msgid "Dependencies" msgstr "Dépendances" @@ -217,7 +217,7 @@ msgstr "Effectuée par" #: re2o/templates/re2o/aff_history.html:35 msgid "Edited" -msgstr "" +msgstr "Modifié" #: re2o/templates/re2o/aff_history.html:36 msgid "Comment" @@ -225,15 +225,15 @@ msgstr "Commentaire" #: re2o/templates/re2o/aff_history.html:48 msgid "Unknown" -msgstr "" +msgstr "Inconnu" #: re2o/templates/re2o/aff_history.html:71 msgid "No event" -msgstr "" +msgstr "Pas d'évènement" #: re2o/templates/re2o/aff_history.html:76 msgid "Related elements" -msgstr "" +msgstr "Éléments liés" #: re2o/templates/re2o/aff_history.html:82 re2o/templates/re2o/history.html:30 msgid "History" @@ -249,10 +249,8 @@ msgid "Contact the organisation %(asso_name)s" msgstr "Contacter l'association %(asso_name)s" #: re2o/templates/re2o/history.html:33 -#, fuzzy, python-format -#| msgid "History of %(object)s" msgid "History of %(title)s" -msgstr "Historique de %(object)s" +msgstr "Historique de %(title)s" #: re2o/templates/re2o/index.html:30 msgid "Home" @@ -329,16 +327,14 @@ msgstr "Tweets de @%(twitter_account_name)s" msgid "Follow @%(twitter_account_name)s" msgstr "Suivre @%(twitter_account_name)s" +#: re2o/templatetags/massive_bootstrap_form.py:413 +msgid "Nothing" +msgstr "Rien" + #: re2o/urls.py:57 msgid "Homepage" msgstr "Page d'accueil" -#: re2o/views.py:96 +#: re2o/views.py:110 re2o/views.py:121 msgid "Unable to get the information." -msgstr "Impossible d'obtenir l'information." - -#~ msgid "Next" -#~ msgstr "Suivant" - -#~ msgid "Previous" -#~ msgstr "Précédent" +msgstr "Impossible d'obtenir l'information." \ No newline at end of file diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 6bcb49f5..e44d110b 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 3579d444..ac667c7b 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,11 +30,11 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: templates/admin/base_site.html:65 templates/base.html:293 +#: templates/admin/base_site.html:65 templates/footer.html:28 msgid "powered by" msgstr "propulsé par" -#: templates/admin/base_site.html:69 templates/base.html:301 +#: templates/admin/base_site.html:69 templates/footer.html:45 msgid "" "This software is under the terms of the GPLv2 License." @@ -80,7 +80,7 @@ msgstr "" msgid "Unknown content" msgstr "" -#: templates/base.html:72 templates/registration/logged_out.html:11 +#: templates/base.html:79 templates/registration/logged_out.html:11 #: templates/registration/password_change_done.html:11 #: templates/registration/password_change_form.html:11 #: templates/registration/password_reset_complete.html:11 @@ -90,148 +90,6 @@ msgstr "" msgid "Home" msgstr "" -#: templates/base.html:93 -msgid "Users" -msgstr "Utilisateurs" - -#: templates/base.html:96 -msgid "Manage the users" -msgstr "Gérer les utilisateurs" - -#: templates/base.html:97 -msgid "Manage the clubs" -msgstr "Gérer les clubs" - -#: templates/base.html:100 -msgid "Manage the machines" -msgstr "Gérer les machines" - -#: templates/base.html:103 -msgid "Manage the subscriptions" -msgstr "Gérer les cotisations" - -#: templates/base.html:116 -msgid "Topology" -msgstr "Topologie" - -#: templates/base.html:118 -msgid "Switches" -msgstr "Commutateurs réseau" - -#: templates/base.html:119 -msgid "Access points" -msgstr "Points d'accès sans fil" - -#: templates/base.html:120 -msgid "Rooms" -msgstr "Chambres" - -#: templates/base.html:130 -msgid "Statistics" -msgstr "Statistiques" - -#: templates/base.html:135 -msgid "Administration" -msgstr "" - -#: templates/base.html:142 -msgid "Information and contact" -msgstr "Informations et contact" - -#: templates/base.html:144 -msgid "About" -msgstr "À propos" - -#: templates/base.html:145 -msgid "Contact" -msgstr "Contact" - -#: templates/base.html:159 -msgid "Sign up" -msgstr "S'inscrire" - -#: templates/base.html:165 templates/registration/login.html:29 -#: templates/registration/login.html:36 -msgid "Log in" -msgstr "" - -#: templates/base.html:173 -msgid "Search" -msgstr "" - -#: templates/base.html:187 -msgid "My profile" -msgstr "Mon profil" - -#: templates/base.html:188 -msgid "Log out" -msgstr "" - -#: templates/base.html:226 -msgid "Username" -msgstr "Pseudo" - -#: templates/base.html:230 -msgid "Room" -msgstr "Chambre" - -#: templates/base.html:234 -msgid "Internet access" -msgstr "Accès Internet" - -#: templates/base.html:237 -#, python-format -msgid "Until %(end_access_date)s" -msgstr "Jusqu'au %(end_access_date)s" - -#: templates/base.html:239 -msgid "Disabled" -msgstr "Désactivé" - -#: templates/base.html:244 -msgid "Membership" -msgstr "Adhésion" - -#: templates/base.html:247 -#, python-format -msgid "Until %(end_adhesion_date)s" -msgstr "Jusqu'au %(end_adhesion_date)s" - -#: templates/base.html:249 -msgid "Non member" -msgstr "Non adhérent" - -#: templates/base.html:257 -msgid "View my profile" -msgstr "Voir mon profil" - -#: templates/base.html:262 -msgid "You are not logged in." -msgstr "Vous n'êtes pas connecté." - -#: templates/base.html:269 -#, python-format -msgid "%(nb)s active machine" -msgid_plural "%(nb)s active machines" -msgstr[0] "%(nb)s machine active" -msgstr[1] "%(nb)s machines actives" - -#: templates/base.html:278 -msgid "View my machines" -msgstr "Voir mes machines" - -#: templates/base.html:291 -msgid "Back to top" -msgstr "Retour en haut" - -#: templates/base.html:295 -msgid "Brought to you with ." -msgstr "Codé avec ." - -#: templates/base.html:298 -msgid "About this website" -msgstr "À propos de ce site" - #: templates/buttons/add.html:27 msgid "Add" msgstr "" @@ -260,6 +118,10 @@ msgstr "Tri décroissant" msgid "Delete" msgstr "" +#: templates/buttons/view.html:25 +msgid "View" +msgstr "" + #: templates/errors/404.html:39 msgid "404 error: page not found" msgstr "Erreur 404 : page non trouvée" @@ -339,6 +201,247 @@ msgstr "Si vous n'avez aucune idée de ce que vous avez fait :" msgid "Go back to a safe page" msgstr "Retourner à une page sécurisée" +#: templates/footer.html:29 +msgid "Brought to you with ." +msgstr "Codé avec ." + +#: templates/footer.html:35 +msgid "About" +msgstr "À propos" + +#: templates/footer.html:42 +msgid "Top" +msgstr "" + +#: templates/nav.html:45 templates/nav.html:52 templates/nav.html:207 +msgid "Users" +msgstr "Utilisateurs" + +#: templates/nav.html:49 +msgid "Users and clubs" +msgstr "" + +#: templates/nav.html:55 +msgid "Clubs" +msgstr "" + +#: templates/nav.html:58 +msgid "Whitelists" +msgstr "" + +#: templates/nav.html:61 +msgid "Bans" +msgstr "" + +#: templates/nav.html:64 +msgid "Massively archive" +msgstr "" + +#: templates/nav.html:70 +msgid "Machines" +msgstr "" + +#: templates/nav.html:75 +msgid "Groups" +msgstr "" + +#: templates/nav.html:76 templates/nav.html:119 +msgid "Advanced" +msgstr "" + +#: templates/nav.html:81 +msgid "Schools" +msgstr "" + +#: templates/nav.html:84 +msgid "Shells" +msgstr "" + +#: templates/nav.html:99 +msgid "Treasury" +msgstr "" + +#: templates/nav.html:102 templates/nav.html:109 +msgid "Invoices" +msgstr "" + +#: templates/nav.html:106 +msgid "Control invoices" +msgstr "" + +#: templates/nav.html:112 +msgid "Cutsom invoices" +msgstr "" + +#: templates/nav.html:117 +msgid "Cost estimates" +msgstr "" + +#: templates/nav.html:124 +msgid "Banks" +msgstr "" + +#: templates/nav.html:127 +msgid "Articles" +msgstr "" + +#: templates/nav.html:130 +msgid "Payment methods" +msgstr "" + +#: templates/nav.html:144 +msgid "Topology" +msgstr "Topologie" + +#: templates/nav.html:147 templates/nav.html:151 +msgid "Switches" +msgstr "Commutateurs réseau" + +#: templates/nav.html:154 +#, fuzzy +#| msgid "Switches" +msgid "Switch models" +msgstr "Commutateurs réseau" + +#: templates/nav.html:157 +#, fuzzy +#| msgid "Switches" +msgid "Switch modules" +msgstr "Commutateurs réseau" + +#: templates/nav.html:159 +#, fuzzy +#| msgid "Switches" +msgid "Switch bays" +msgstr "Commutateurs réseau" + +#: templates/nav.html:161 +msgid "Stacks" +msgstr "" + +#: templates/nav.html:164 +#, fuzzy +#| msgid "My profile" +msgid "Port profiles" +msgstr "Mon profil" + +#: templates/nav.html:168 +msgid "Infrastructure" +msgstr "" + +#: templates/nav.html:172 +msgid "Dormitories" +msgstr "" + +#: templates/nav.html:174 +msgid "Buildings" +msgstr "" + +#: templates/nav.html:177 +msgid "Rooms" +msgstr "Chambres" + +#: templates/nav.html:181 +msgid "Access points" +msgstr "Points d'accès sans fil" + +#: templates/nav.html:194 +msgid "Statistics" +msgstr "Statistiques" + +#: templates/nav.html:197 +msgid "Summary" +msgstr "" + +#: templates/nav.html:199 +msgid "Events" +msgstr "" + +#: templates/nav.html:201 templates/nav.html:226 +msgid "General" +msgstr "" + +#: templates/nav.html:203 +msgid "Database" +msgstr "" + +#: templates/nav.html:205 +msgid "Wiring actions" +msgstr "" + +#: templates/nav.html:209 +msgid "Machine history" +msgstr "" + +#: templates/nav.html:221 +msgid "Administration" +msgstr "" + +#: templates/nav.html:231 +msgid "LDAP service users" +msgstr "" + +#: templates/nav.html:236 +msgid "Services" +msgstr "" + +#: templates/nav.html:239 +msgid "Machine types" +msgstr "" + +#: templates/nav.html:241 +msgid "Network" +msgstr "" + +#: templates/nav.html:245 +msgid "IP ranges" +msgstr "" + +#: templates/nav.html:248 +msgid "VLANs" +msgstr "" + +#: templates/nav.html:251 +msgid "Extensions and zones" +msgstr "" + +#: templates/nav.html:254 +msgid "NAS" +msgstr "" + +#: templates/nav.html:257 +msgid "Server roles" +msgstr "" + +#: templates/nav.html:260 +msgid "Ports openings" +msgstr "" + +#: templates/nav.html:269 +msgid "Contact" +msgstr "Contact" + +#: templates/nav.html:277 +msgid "Sign up" +msgstr "S'inscrire" + +#: templates/nav.html:283 templates/registration/login.html:29 +#: templates/registration/login.html:36 +msgid "Log in" +msgstr "" + +#: templates/nav.html:291 +msgid "Search" +msgstr "" + +#: templates/nav.html:310 +msgid "My profile" +msgstr "Mon profil" + +#: templates/nav.html:312 +msgid "Log out" +msgstr "" + #: templates/pagination.html:35 msgid "First" msgstr "Première page" @@ -401,3 +504,87 @@ msgstr "" #, python-format msgid "The %(site_name)s team" msgstr "" + +#: templates/sidebar.html:34 templates/sidebar.html:74 +msgid "Username" +msgstr "Pseudo" + +#: templates/sidebar.html:41 templates/sidebar.html:78 +msgid "Room" +msgstr "Chambre" + +#: templates/sidebar.html:48 templates/sidebar.html:82 +msgid "Internet access" +msgstr "Accès Internet" + +#: templates/sidebar.html:51 templates/sidebar.html:85 +#, fuzzy, python-format +#| msgid "Until %(end_access_date)s" +msgid "" +"Until\n" +" %(end_access_date)s" +msgstr "Jusqu'au %(end_access_date)s" + +#: templates/sidebar.html:54 templates/sidebar.html:88 +msgid "Disabled" +msgstr "Désactivé" + +#: templates/sidebar.html:60 templates/sidebar.html:93 +msgid "Membership" +msgstr "Adhésion" + +#: templates/sidebar.html:63 templates/sidebar.html:96 +#, fuzzy, python-format +#| msgid "Until %(end_adhesion_date)s" +msgid "" +"Until\n" +" %(end_adhesion_date)s" +msgstr "Jusqu'au %(end_adhesion_date)s" + +#: templates/sidebar.html:66 templates/sidebar.html:99 +msgid "Non member" +msgstr "Non adhérent" + +#: templates/sidebar.html:108 +msgid "View my profile" +msgstr "Voir mon profil" + +#: templates/sidebar.html:113 +msgid "You are not logged in." +msgstr "Vous n'êtes pas connecté." + +#: templates/sidebar.html:120 +#, fuzzy, python-format +#| msgid "%(nb)s active machine" +#| msgid_plural "%(nb)s active machines" +msgid "%(nb)s active machine" +msgid_plural "" +"%(nb)s\n" +" active machines" +msgstr[0] "%(nb)s machine active" +msgstr[1] "%(nb)s machines actives" + +#: templates/sidebar.html:131 +msgid "View my machines" +msgstr "Voir mes machines" + +#~ msgid "Manage the users" +#~ msgstr "Gérer les utilisateurs" + +#~ msgid "Manage the clubs" +#~ msgstr "Gérer les clubs" + +#~ msgid "Manage the machines" +#~ msgstr "Gérer les machines" + +#~ msgid "Manage the subscriptions" +#~ msgstr "Gérer les cotisations" + +#~ msgid "Information and contact" +#~ msgstr "Informations et contact" + +#~ msgid "Back to top" +#~ msgstr "Retour en haut" + +#~ msgid "About this website" +#~ msgstr "À propos de ce site" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 6ad77e5c..6067d18e 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,61 +34,61 @@ msgstr "" msgid "comment" msgstr "commentaire" -#: tickets/models.py:59 +#: tickets/models.py:75 msgid "Title of the ticket." msgstr "Titre du ticket." -#: tickets/models.py:68 +#: tickets/models.py:84 msgid "An email address to get back to you." msgstr "Une adresse mail pour vous recontacter." -#: tickets/models.py:72 +#: tickets/models.py:88 msgid "Language of the ticket." msgstr "Langue des tickets" -#: tickets/models.py:77 tickets/models.py:174 +#: tickets/models.py:93 tickets/models.py:204 msgid "Can view a ticket object" msgstr "Peut voir un objet ticket" -#: tickets/models.py:78 tickets/models.py:175 +#: tickets/models.py:94 tickets/models.py:205 msgid "ticket" msgstr "ticket" -#: tickets/models.py:79 tickets/models.py:176 +#: tickets/models.py:95 tickets/models.py:206 msgid "tickets" msgstr "tickets" -#: tickets/models.py:83 +#: tickets/models.py:99 #, python-brace-format msgid "Ticket from {name}. Date: {date}." msgstr "Ticket de {name}. Date : {date}." -#: tickets/models.py:85 +#: tickets/models.py:101 #, python-format msgid "Anonymous ticket. Date: %s." msgstr "Ticket anonyme. Date : %s." -#: tickets/models.py:93 tickets/templates/tickets/aff_ticket.html:52 +#: tickets/models.py:109 tickets/templates/tickets/aff_ticket.html:52 msgid "Anonymous user" msgstr "Utilisateur anonyme" -#: tickets/models.py:131 +#: tickets/models.py:150 msgid "You don't have the right to view other tickets than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:143 tickets/models.py:218 +#: tickets/models.py:162 tickets/models.py:248 msgid "You don't have the right to view the list of tickets." msgstr "Vous n'avez pas le droit de voir la liste des tickets." -#: tickets/models.py:191 +#: tickets/models.py:221 msgid "You don't have the right to view other tickets comments than yours." msgstr "Vous n'avez pas le droit de voir d'autres tickets que les vôtres." -#: tickets/models.py:206 +#: tickets/models.py:236 msgid "You don't have the right to edit other tickets comments than yours." msgstr "Vous n'avez pas le droit d'éditer d'autres tickets que les vôtres." -#: tickets/models.py:237 +#: tickets/models.py:269 msgid "Update of your ticket" msgstr "Mise à jour de votre ticket" @@ -111,14 +111,24 @@ msgstr "Options des tickets" msgid "Can view tickets options" msgstr "Peut voir les options des tickets" +#: tickets/templates/tickets/aff_profil.html:6 #: tickets/templates/tickets/aff_ticket.html:32 -#: tickets/templates/tickets/contact.html:4 +#: tickets/templates/tickets/contact.html:8 #: tickets/templates/tickets/index.html:29 #: tickets/templates/tickets/preferences.html:6 -#: tickets/templates/tickets/profil.html:6 msgid "Tickets" msgstr "Tickets" +#: tickets/templates/tickets/aff_profil.html:12 +#: tickets/templates/tickets/contact.html:11 +#: tickets/templates/tickets/navbar_logout.html:4 +msgid "Open a ticket" +msgstr "Ouvrir un ticket" + +#: tickets/templates/tickets/aff_profil.html:19 +msgid "No tickets" +msgstr "Pas de tickets" + #: tickets/templates/tickets/aff_ticket.html:36 #, python-format msgid "Ticket #%(id)s" @@ -150,7 +160,7 @@ msgid "Add a comment " msgstr "Ajouter un commentaire" #: tickets/templates/tickets/aff_ticket.html:64 -#: tickets/templates/tickets/preferences.html:14 tickets/views.py:153 +#: tickets/templates/tickets/preferences.html:14 tickets/views.py:149 msgid "Edit" msgstr "Modifier" @@ -228,7 +238,7 @@ msgstr "Date" msgid "Anonymous" msgstr "Anonyme" -#: tickets/templates/tickets/contact.html:8 +#: tickets/templates/tickets/contact.html:17 #, python-format msgid "" "If you are experiencing issues with the services offered by %(asso_name)s, " @@ -240,12 +250,6 @@ msgstr "" "voulez nous contacter pour n'importe quel autre sujet, veuillez choisir une " "adresse ci-dessous." -#: tickets/templates/tickets/contact.html:10 -#: tickets/templates/tickets/navbar_logout.html:4 -#: tickets/templates/tickets/profil.html:12 -msgid "Open a ticket" -msgstr "Ouvrir un ticket" - #: tickets/templates/tickets/delete.html:29 msgid "Deletion of tickets" msgstr "Suppression de tickets" @@ -256,6 +260,8 @@ msgid "" "Warning: are you sure you want to delete this %(objet_name)s object " "( %(objet)s )?" msgstr "" +"Attention: êtes-vous sûr de vouloir supprimer l'objet %(object_name)s " +"( %(objet)s )?" #: tickets/templates/tickets/delete.html:36 msgid "Confirm" @@ -311,42 +317,38 @@ msgstr "Adresse mail de publication" msgid "No email address, the tickets will not be published." msgstr "Pas d'adresse mail, les tickets ne seront pas publiés." -#: tickets/templates/tickets/profil.html:19 -msgid "No tickets" -msgstr "Pas de tickets" - -#: tickets/views.py:62 +#: tickets/views.py:58 msgid "" "Your ticket has been succesfully opened. We will take care of it as soon as " "possible." msgstr "" "Votre ticket a bien été ouvert. Nous nous en occuperons dès que possible." -#: tickets/views.py:109 +#: tickets/views.py:105 msgid "Ticket has been updated successfully" msgstr "Le ticket a été mis à jour" -#: tickets/views.py:130 +#: tickets/views.py:126 msgid "This comment was added." msgstr "Le commentaire a été ajouté" -#: tickets/views.py:135 +#: tickets/views.py:131 msgid "Add a comment" msgstr "Ajouter un commentaire" -#: tickets/views.py:148 +#: tickets/views.py:144 msgid "This comment was edited." msgstr "Le commentaire a été édité" -#: tickets/views.py:164 +#: tickets/views.py:160 msgid "The comment was deleted." msgstr "Le commentaire a été supprimé" -#: tickets/views.py:169 +#: tickets/views.py:165 msgid "Ticket Comment" msgstr "Commentaire de ticket" -#: tickets/views.py:183 tickets/views.py:208 +#: tickets/views.py:179 tickets/views.py:204 msgid "Never" msgstr "Jamais" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index e5621926..a8010f37 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: \n" "MIME-Version: 1.0\n" @@ -34,264 +34,264 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: topologie/forms.py:202 +#: topologie/forms.py:192 msgid "Start:" msgstr "Début :" -#: topologie/forms.py:203 +#: topologie/forms.py:193 msgid "End:" msgstr "Fin :" -#: topologie/models.py:70 +#: topologie/models.py:73 msgid "Can view a stack object" msgstr "Peut voir un objet pile" -#: topologie/models.py:71 +#: topologie/models.py:74 msgid "switches stack" msgstr "pile de commutateurs réseau" -#: topologie/models.py:72 +#: topologie/models.py:75 msgid "switches stacks" msgstr "piles de commutateurs réseau" -#: topologie/models.py:87 +#: topologie/models.py:90 msgid "The maximum ID is less than the minimum ID." msgstr "L'ID maximum est inférieur l'ID minimum." -#: topologie/models.py:99 +#: topologie/models.py:103 msgid "Details about the AP's location." msgstr "Détails sur l'emplacement du point d'accès sans fil." -#: topologie/models.py:105 +#: topologie/models.py:109 msgid "Can view an access point object" msgstr "Peut voir un objet point d'accès sans fil" -#: topologie/models.py:106 topologie/views.py:664 topologie/views.py:716 +#: topologie/models.py:110 topologie/views.py:696 topologie/views.py:747 msgid "access point" msgstr "point d'accès sans fil" -#: topologie/models.py:107 +#: topologie/models.py:111 msgid "access points" msgstr "points d'accès sans fil" -#: topologie/models.py:232 +#: topologie/models.py:250 msgid "Number of ports." msgstr "Nombre de ports." -#: topologie/models.py:242 +#: topologie/models.py:260 msgid "Switch model." msgstr "Modèle de commutateur réseau." -#: topologie/models.py:252 +#: topologie/models.py:270 msgid "RADIUS key of the switch." msgstr "Clé RADIUS du commutateur réseau." -#: topologie/models.py:259 +#: topologie/models.py:277 msgid "Management credentials for the switch." msgstr "Identifiants de gestion du commutateur réseau." -#: topologie/models.py:262 +#: topologie/models.py:280 msgid "Automatic provision for the switch." msgstr "Provision automatique pour le commutateur réseau." -#: topologie/models.py:267 +#: topologie/models.py:285 msgid "Can view a switch object" msgstr "Peut voir un objet commutateur réseau" -#: topologie/models.py:268 topologie/views.py:542 topologie/views.py:615 +#: topologie/models.py:286 topologie/views.py:570 topologie/views.py:645 msgid "switch" msgstr "commutateur réseau" -#: topologie/models.py:269 +#: topologie/models.py:287 msgid "switches" msgstr "commutateurs réseau" -#: topologie/models.py:283 +#: topologie/models.py:302 msgid "The switch ID exceeds the limits allowed by the stack." msgstr "L'ID du commutateur réseau dépasse les bornes autorisées par la pile." -#: topologie/models.py:290 +#: topologie/models.py:309 msgid "The stack member ID can't be void." msgstr "L'ID de membre dans la pile ne peut-être vide." -#: topologie/models.py:297 +#: topologie/models.py:320 msgid "The end port is less than the start port." msgstr "Le port de fin est inférieur au port de début." -#: topologie/models.py:305 +#: topologie/models.py:328 msgid "This switch can't have that many ports." msgstr "Ce commutateur réseau ne peut pas avoir autant de ports." -#: topologie/models.py:534 +#: topologie/models.py:586 msgid "The switch model is modular." msgstr "Le modèle de commutateur réseau est modulaire." -#: topologie/models.py:537 +#: topologie/models.py:589 msgid "The switch is considered as a module." msgstr "Le commutateur réseau est considéré comme un module." -#: topologie/models.py:541 +#: topologie/models.py:593 msgid "Can view a switch model object" msgstr "Peut voir un objet modèle de commutateur réseau" -#: topologie/models.py:542 topologie/views.py:832 +#: topologie/models.py:594 topologie/views.py:862 msgid "switch model" msgstr "modèle de commutateur réseau" -#: topologie/models.py:543 +#: topologie/models.py:595 msgid "switch models" msgstr "modèles de commutateur réseau" -#: topologie/models.py:557 +#: topologie/models.py:614 msgid "Reference of a module." msgstr "Référence d'un module." -#: topologie/models.py:558 +#: topologie/models.py:615 msgid "module reference" msgstr "référence de module" -#: topologie/models.py:564 +#: topologie/models.py:621 msgid "Comment." msgstr "Commentaire." -#: topologie/models.py:565 +#: topologie/models.py:622 msgid "comment" msgstr "commentaire" -#: topologie/models.py:569 +#: topologie/models.py:626 msgid "Can view a switch module object" msgstr "Peut voir un objet module de commutateur réseau" -#: topologie/models.py:570 +#: topologie/models.py:627 msgid "switch module" msgstr "module de commutateur réseau" -#: topologie/models.py:571 +#: topologie/models.py:628 msgid "switch modules" msgstr "modules de commutateur réseau" -#: topologie/models.py:583 +#: topologie/models.py:646 msgid "Slot on switch." msgstr "Emplacement sur le commutateur réseau." -#: topologie/models.py:583 +#: topologie/models.py:646 msgid "slot" msgstr "emplacement " -#: topologie/models.py:590 +#: topologie/models.py:653 msgid "Can view a link between switch and module object" msgstr "Peut voir un objet lien entre commutateur réseau et module" -#: topologie/models.py:593 +#: topologie/models.py:656 msgid "link between switch and module" msgstr "lien entre commutateur réseau et module" -#: topologie/models.py:594 +#: topologie/models.py:657 msgid "links between switch and module" msgstr "liens entre commutateur réseau et module" -#: topologie/models.py:598 +#: topologie/models.py:661 #, python-format msgid "On slot %(slot)s of %(switch)s" msgstr "Sur l'emplacement %(slot)s de %(switch)s" -#: topologie/models.py:610 +#: topologie/models.py:677 msgid "Can view a switch constructor object" msgstr "Peut voir un objet constructeur de commutateur réseau" -#: topologie/models.py:612 topologie/views.py:1079 +#: topologie/models.py:679 topologie/views.py:1102 msgid "switch constructor" msgstr "constructeur de commutateur réseau" -#: topologie/models.py:613 +#: topologie/models.py:680 msgid "switch constructors" msgstr "constructeurs de commutateur réseau" -#: topologie/models.py:627 +#: topologie/models.py:700 msgid "Can view a switch bay object" msgstr "Peut voir un objet baie de brassage" -#: topologie/models.py:628 topologie/views.py:892 +#: topologie/models.py:701 topologie/views.py:922 msgid "switch bay" msgstr "baie de brassage" -#: topologie/models.py:629 +#: topologie/models.py:702 msgid "switch bays" msgstr "baies de brassage" -#: topologie/models.py:642 +#: topologie/models.py:718 msgid "Can view a dormitory object" msgstr "Peut voir un objet résidence" -#: topologie/models.py:643 topologie/views.py:1016 +#: topologie/models.py:719 topologie/views.py:1040 msgid "dormitory" msgstr "résidence" -#: topologie/models.py:644 +#: topologie/models.py:720 msgid "dormitories" msgstr "résidences" -#: topologie/models.py:670 +#: topologie/models.py:751 msgid "Can view a building object" msgstr "Peut voir un objet bâtiment" -#: topologie/models.py:671 topologie/views.py:953 +#: topologie/models.py:752 topologie/views.py:980 msgid "building" msgstr "bâtiment" -#: topologie/models.py:672 +#: topologie/models.py:753 msgid "buildings" msgstr "bâtiments" -#: topologie/models.py:723 +#: topologie/models.py:815 msgid "Port state Active." msgstr "État du port Actif." -#: topologie/models.py:724 +#: topologie/models.py:816 msgid "port state Active" msgstr "état du port Actif" -#: topologie/models.py:730 +#: topologie/models.py:822 msgid "Can view a port object" msgstr "Peut voir un objet port" -#: topologie/models.py:731 +#: topologie/models.py:823 msgid "port" msgstr "port" -#: topologie/models.py:732 +#: topologie/models.py:824 msgid "ports" msgstr "ports" -#: topologie/models.py:738 +#: topologie/models.py:830 msgid "Uplink: " msgstr "Liaison montante : " -#: topologie/models.py:740 +#: topologie/models.py:832 msgid "Machine: " msgstr "Machine : " -#: topologie/models.py:742 +#: topologie/models.py:834 msgid "Room: " msgstr "Chambre : " -#: topologie/models.py:744 +#: topologie/models.py:836 msgid "Unknown" msgstr "Inconnu" -#: topologie/models.py:805 +#: topologie/models.py:895 msgid "The port can't exist, its number is too great." msgstr "Le port ne peut pas exister, son numéro est trop grand." -#: topologie/models.py:816 +#: topologie/models.py:906 msgid "Room, interface and related port are mutually exclusive." msgstr "Chambre, interface et port relié sont mutuellement exclusifs." -#: topologie/models.py:819 +#: topologie/models.py:909 msgid "A port can't be related to itself." msgstr "Un port ne peut être relié à lui-même." -#: topologie/models.py:824 +#: topologie/models.py:914 msgid "" "The related port is already used, please clear it before creating the " "relation." @@ -299,152 +299,152 @@ msgstr "" "Le port relié est déjà utilisé, veuillez le modifier avant de créer la " "relation." -#: topologie/models.py:846 +#: topologie/models.py:942 msgid "Can view a room object" msgstr "Peut voir un objet chambre" -#: topologie/models.py:847 topologie/views.py:773 +#: topologie/models.py:943 topologie/views.py:804 msgid "room" msgstr "chambre" -#: topologie/models.py:848 +#: topologie/models.py:944 msgid "rooms" msgstr "chambres" -#: topologie/models.py:858 +#: topologie/models.py:975 msgid "MAC-RADIUS" msgstr "MAC-RADIUS" -#: topologie/models.py:871 topologie/templates/topologie/aff_chambres.html:36 +#: topologie/models.py:988 topologie/templates/topologie/aff_chambres.html:36 #: topologie/templates/topologie/aff_port.html:38 msgid "Room" msgstr "Chambre" -#: topologie/models.py:872 topologie/templates/topologie/aff_ap.html:36 +#: topologie/models.py:989 topologie/templates/topologie/aff_ap.html:36 msgid "Access point" msgstr "Point d'accès sans fil" -#: topologie/models.py:873 +#: topologie/models.py:990 msgid "Uplink" msgstr "Liaison montante" -#: topologie/models.py:874 +#: topologie/models.py:991 msgid "Organisation machine" msgstr "Machine d'association" -#: topologie/models.py:875 +#: topologie/models.py:992 msgid "Nothing" msgstr "Rien" -#: topologie/models.py:877 +#: topologie/models.py:994 msgid "name" msgstr "nom" -#: topologie/models.py:883 +#: topologie/models.py:1000 msgid "default profile" msgstr "profil par défaut" -#: topologie/models.py:891 +#: topologie/models.py:1008 msgid "profile on dormitory" msgstr "profil sur la résidence" -#: topologie/models.py:899 +#: topologie/models.py:1016 msgid "VLAN untagged" msgstr "VLAN untagged" -#: topologie/models.py:905 +#: topologie/models.py:1022 msgid "VLAN(s) tagged" msgstr "VLAN(s) tagged" -#: topologie/models.py:910 +#: topologie/models.py:1027 msgid "Type of RADIUS authentication: inactive, MAC-address or 802.1X." msgstr "Type d'authentification RADIUS : inactive, MAC-address ou 802.1X." -#: topologie/models.py:911 +#: topologie/models.py:1028 msgid "RADIUS type" msgstr "Type de RADIUS" -#: topologie/models.py:918 +#: topologie/models.py:1035 msgid "In case of MAC-authentication: mode COMMON or STRICT on this port." msgstr "" "Dans le cas d'authentification par adresse MAC : mode COMMON ou STRICT sur " "ce port." -#: topologie/models.py:920 +#: topologie/models.py:1037 msgid "RADIUS mode" msgstr "Mode de RADIUS" -#: topologie/models.py:923 +#: topologie/models.py:1040 msgid "Port speed limit." msgstr "Limite de vitesse du port." -#: topologie/models.py:928 +#: topologie/models.py:1045 msgid "Limit of MAC-address on this port." msgstr "Limite de MAC-address sur ce port." -#: topologie/models.py:929 +#: topologie/models.py:1046 msgid "MAC limit" msgstr "Limite MAC" -#: topologie/models.py:931 +#: topologie/models.py:1048 msgid "Flow control." msgstr "Contrôle du flux." -#: topologie/models.py:934 +#: topologie/models.py:1051 msgid "Protect against rogue DHCP." msgstr "Protège contre les DHCP pirates." -#: topologie/models.py:935 +#: topologie/models.py:1052 #: topologie/templates/topologie/aff_vlanoptions.html:36 msgid "DHCP snooping" msgstr "DHCP snooping" -#: topologie/models.py:939 +#: topologie/models.py:1056 msgid "Protect against rogue DHCPv6." msgstr "Protège contre les DHCPv6 pirates." -#: topologie/models.py:940 +#: topologie/models.py:1057 #: topologie/templates/topologie/aff_vlanoptions.html:37 msgid "DHCPv6 snooping" msgstr "DHCPv6 snooping" -#: topologie/models.py:944 +#: topologie/models.py:1061 msgid "Check if IP address is DHCP assigned." msgstr "Vérifie si l'adresse IP est attribuée par DHCP." -#: topologie/models.py:945 +#: topologie/models.py:1062 msgid "ARP protection" msgstr "Protection ARP" -#: topologie/models.py:949 +#: topologie/models.py:1066 msgid "Protect against rogue RA." msgstr "Protège contre les RA pirates." -#: topologie/models.py:950 +#: topologie/models.py:1067 msgid "RA guard" msgstr "RA guard" -#: topologie/models.py:954 +#: topologie/models.py:1071 msgid "Protect against loop." msgstr "Protège contre une boucle." -#: topologie/models.py:955 +#: topologie/models.py:1072 msgid "loop protection" msgstr "protection contre une boucle" -#: topologie/models.py:959 +#: topologie/models.py:1076 msgid "Can view a port profile object" msgstr "Peut voir un objet profil de port" -#: topologie/models.py:960 topologie/views.py:1130 +#: topologie/models.py:1077 topologie/views.py:1153 msgid "port profile" msgstr "profil de port" -#: topologie/models.py:961 +#: topologie/models.py:1078 msgid "port profiles" msgstr "profils de port" -#: topologie/models.py:999 +#: topologie/models.py:1116 msgid "A default profile for all dormitories of that type already exists." msgstr "" "Un profil par défaut pour toutes les résidences de ce type existe déjà. " @@ -454,7 +454,7 @@ msgid "MAC address" msgstr "Adresse MAC" #: topologie/templates/topologie/aff_ap.html:40 -#: topologie/templates/topologie/aff_switch.html:39 +#: topologie/templates/topologie/aff_switch.html:66 msgid "IPv4 address" msgstr "Adresse IPv4" @@ -462,7 +462,6 @@ msgstr "Adresse IPv4" #: topologie/templates/topologie/aff_chambres.html:40 #: topologie/templates/topologie/aff_port.html:46 #: topologie/templates/topologie/aff_stacks.html:36 -#: topologie/templates/topologie/aff_switch.html:49 #: topologie/templates/topologie/edit_stack_sw.html:34 msgid "Details" msgstr "Détails" @@ -479,7 +478,6 @@ msgstr "Bâtiment" #: topologie/templates/topologie/aff_building.html:38 #: topologie/templates/topologie/index_ap.html:33 -#: topologie/templates/topologie/sidebar.html:47 msgid "Access points" msgstr "Points d'accès sans fil" @@ -493,7 +491,7 @@ msgid "Dormitory" msgstr "Résidence" #: topologie/templates/topologie/aff_dormitory.html:38 -#: topologie/templates/topologie/index_physical_grouping.html:50 +#: topologie/templates/topologie/index_building.html:33 msgid "Buildings" msgstr "Bâtiments" @@ -514,7 +512,6 @@ msgstr "Micrologiciel" #: topologie/templates/topologie/aff_model_switch.html:42 #: topologie/templates/topologie/aff_modules.html:38 #: topologie/templates/topologie/index.html:66 -#: topologie/templates/topologie/sidebar.html:35 msgid "Switches" msgstr "Commutateurs réseau" @@ -572,67 +569,66 @@ msgstr "Désactivé" msgid "Default: " msgstr "Par défaut : " -#: topologie/templates/topologie/aff_port_profile.html:37 -#: topologie/templates/topologie/aff_vlanoptions.html:34 -msgid "Name" -msgstr "Nom" - -#: topologie/templates/topologie/aff_port_profile.html:38 +#: topologie/templates/topologie/aff_port_profile.html:57 msgid "Default for" msgstr "Par défaut pour" -#: topologie/templates/topologie/aff_port_profile.html:39 -msgid "VLANs" -msgstr "VLANs" - -#: topologie/templates/topologie/aff_port_profile.html:40 +#: topologie/templates/topologie/aff_port_profile.html:58 msgid "RADIUS settings" msgstr "Paramètres RADIUS" -#: topologie/templates/topologie/aff_port_profile.html:41 +#: topologie/templates/topologie/aff_port_profile.html:59 msgid "Speed limit" msgstr "Limite de vitesse" -#: topologie/templates/topologie/aff_port_profile.html:42 +#: topologie/templates/topologie/aff_port_profile.html:60 msgid "MAC address limit" msgstr "Limite d'adresse MAC" -#: topologie/templates/topologie/aff_port_profile.html:43 -msgid "Security" -msgstr "Sécurité" +#: topologie/templates/topologie/aff_port_profile.html:67 +msgid " on %(dorm)s" +msgstr " sur %(dorm)s" -#: topologie/templates/topologie/aff_port_profile.html:50 -#, python-format -msgid " on %(dorm)s" -msgstr " sur %(dorm)s" - -#: topologie/templates/topologie/aff_port_profile.html:50 +#: topologie/templates/topologie/aff_port_profile.html:67 msgid "Everywhere" msgstr "Partout" -#: topologie/templates/topologie/aff_port_profile.html:53 -msgid "Untagged: " -msgstr "Untagged :" - -#: topologie/templates/topologie/aff_port_profile.html:57 -msgid "Tagged: " -msgstr "Tagged : " - -#: topologie/templates/topologie/aff_port_profile.html:61 +#: topologie/templates/topologie/aff_port_profile.html:71 msgid "RADIUS type: " msgstr "Type de RADIUS : " -#: topologie/templates/topologie/aff_port_profile.html:64 +#: topologie/templates/topologie/aff_port_profile.html:74 msgid "RADIUS mode: " msgstr "Mode de RADIUS : " +#: topologie/templates/topologie/aff_port_profile.html:84 +msgid "Security" +msgstr "Sécurité" + +#: topologie/templates/topologie/aff_port_profile.html:91 +msgid "VLANs" +msgstr "VLANs" + +#: topologie/templates/topologie/aff_port_profile.html:97 +msgid "Untagged: " +msgstr "Untagged :" + +#: topologie/templates/topologie/aff_port_profile.html:103 +#: topologie/templates/topologie/aff_port_profile.html:115 +msgid "None" +msgstr "Aucun" + +#: topologie/templates/topologie/aff_port_profile.html:109 +msgid "Tagged: " +msgstr "Tagged : " + #: topologie/templates/topologie/aff_repr_switch.html:67 #: topologie/templates/topologie/aff_repr_switch.html:110 msgid "Empty" msgstr "Vide" #: topologie/templates/topologie/aff_stacks.html:32 -#: topologie/templates/topologie/aff_switch.html:45 +#: topologie/templates/topologie/aff_switch.html:76 #: topologie/templates/topologie/edit_stack_sw.html:32 #: topologie/templates/topologie/index_p.html:39 msgid "Stack" @@ -647,33 +643,32 @@ msgstr "ID" msgid "Members" msgstr "Membres" -#: topologie/templates/topologie/aff_switch.html:37 -#: topologie/templates/topologie/topo_more.html:56 -msgid "DNS name" -msgstr "Nom DNS" - -#: topologie/templates/topologie/aff_switch.html:41 +#: topologie/templates/topologie/aff_switch.html:44 #: topologie/templates/topologie/aff_switch_bay.html:36 #: topologie/templates/topologie/index_p.html:37 msgid "Switch bay" msgstr "Baie de brassage" -#: topologie/templates/topologie/aff_switch.html:43 +#: topologie/templates/topologie/aff_switch.html:55 +msgid "Creation of ports" +msgstr "Création de ports" + +#: topologie/templates/topologie/aff_switch.html:71 msgid "Ports" msgstr "Ports" -#: topologie/templates/topologie/aff_switch.html:47 +#: topologie/templates/topologie/aff_switch.html:81 msgid "Stack member ID" msgstr "ID de membre dans la pile" -#: topologie/templates/topologie/aff_switch.html:48 +#: topologie/templates/topologie/aff_switch.html:86 #: topologie/templates/topologie/index_p.html:38 msgid "Switch model" msgstr "Modèle de commutateur réseau" -#: topologie/templates/topologie/aff_switch.html:76 -msgid "Creation of ports" -msgstr "Création de ports" +#: topologie/templates/topologie/aff_switch.html:95 +msgid "Details: " +msgstr "Détails : " #: topologie/templates/topologie/aff_switch_bay.html:40 msgid "Information" @@ -683,6 +678,10 @@ msgstr "Informations" msgid "Switches of the bay" msgstr "Commutateurs réseau de la baie" +#: topologie/templates/topologie/aff_vlanoptions.html:34 +msgid "Name" +msgstr "Nom" + #: topologie/templates/topologie/aff_vlanoptions.html:35 msgid "ARP protect" msgstr "Protection ARP" @@ -698,12 +697,15 @@ msgstr "MLD" #: topologie/templates/topologie/delete.html:29 #: topologie/templates/topologie/index.html:30 #: topologie/templates/topologie/index_ap.html:30 +#: topologie/templates/topologie/index_building.html:30 +#: topologie/templates/topologie/index_dormitory.html:30 #: topologie/templates/topologie/index_model_switch.html:30 #: topologie/templates/topologie/index_module.html:30 #: topologie/templates/topologie/index_p.html:30 -#: topologie/templates/topologie/index_physical_grouping.html:30 #: topologie/templates/topologie/index_portprofile.html:29 #: topologie/templates/topologie/index_room.html:30 +#: topologie/templates/topologie/index_stack.html:30 +#: topologie/templates/topologie/index_switch_bay.html:30 #: topologie/templates/topologie/switch.html:30 #: topologie/templates/topologie/topo.html:30 #: topologie/templates/topologie/topo_more.html:30 @@ -741,6 +743,18 @@ msgstr "Ajouter un commutateur réseau" msgid "Add an access point" msgstr "Ajouter un point d'accès sans fil" +#: topologie/templates/topologie/index_building.html:36 +msgid "Add a building" +msgstr "Ajouter un bâtiment" + +#: topologie/templates/topologie/index_dormitory.html:33 +msgid "Dormitories" +msgstr "Résidences" + +#: topologie/templates/topologie/index_dormitory.html:36 +msgid "Add a dormitory" +msgstr "Ajouter une résidence" + #: topologie/templates/topologie/index_model_switch.html:33 msgid "Switch models" msgstr "Modèles de commutateur réseau" @@ -758,7 +772,6 @@ msgid "Add a switch constructor" msgstr "Ajouter un constructeur de commutateur réseau" #: topologie/templates/topologie/index_module.html:33 -#: topologie/templates/topologie/sidebar.html:39 msgid "Switch modules" msgstr "Modules de commutateur réseau" @@ -778,36 +791,7 @@ msgstr "Ajouter un port" msgid "Add ports to the port list" msgstr "Ajouter des ports à la liste des ports" -#: topologie/templates/topologie/index_physical_grouping.html:33 -msgid "Stacks" -msgstr "Piles" - -#: topologie/templates/topologie/index_physical_grouping.html:36 -msgid "Add a stack" -msgstr "Ajouter une pile" - -#: topologie/templates/topologie/index_physical_grouping.html:41 -msgid "Switch bays" -msgstr "Baies de brassage" - -#: topologie/templates/topologie/index_physical_grouping.html:44 -msgid "Add a switch bay" -msgstr "Ajouter une baie de brassage" - -#: topologie/templates/topologie/index_physical_grouping.html:53 -msgid "Add a building" -msgstr "Ajouter un bâtiment" - -#: topologie/templates/topologie/index_physical_grouping.html:60 -msgid "Dormitories" -msgstr "Résidences" - -#: topologie/templates/topologie/index_physical_grouping.html:63 -msgid "Add a dormitory" -msgstr "Ajouter une résidence" - #: topologie/templates/topologie/index_portprofile.html:34 -#: topologie/templates/topologie/sidebar.html:43 msgid "Port profiles" msgstr "Profils de port" @@ -827,17 +811,21 @@ msgstr "Chambres" msgid "Add a room" msgstr "Ajouter une chambre" -#: topologie/templates/topologie/sidebar.html:31 -msgid "Rooms and premises" -msgstr "Chambres et locaux" +#: topologie/templates/topologie/index_stack.html:33 +msgid "Stacks" +msgstr "Piles" -#: topologie/templates/topologie/sidebar.html:51 -msgid "Physical grouping" -msgstr "Groupements physiques" +#: topologie/templates/topologie/index_stack.html:36 +msgid "Add a stack" +msgstr "Ajouter une pile" -#: topologie/templates/topologie/sidebar.html:55 -msgid "Switch models and constructors" -msgstr "Modèles et constructeurs de commutateur réseau" +#: topologie/templates/topologie/index_switch_bay.html:33 +msgid "Switch bays" +msgstr "Baies de brassage" + +#: topologie/templates/topologie/index_switch_bay.html:36 +msgid "Add a switch bay" +msgstr "Ajouter une baie de brassage" #: topologie/templates/topologie/switch.html:39 #: topologie/templates/topologie/topo.html:36 @@ -858,68 +846,72 @@ msgstr "Réglages spécifiques pour l'objet %(device)s" msgid "General settings for the machine linked to the %(device)s object" msgstr "Réglages généraux pour la machine liée à l'objet %(device)s" -#: topologie/views.py:350 +#: topologie/templates/topologie/topo_more.html:56 +msgid "DNS name" +msgstr "Nom DNS" + +#: topologie/views.py:375 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: topologie/views.py:353 topologie/views.py:403 topologie/views.py:460 -#: topologie/views.py:748 topologie/views.py:805 topologie/views.py:865 -#: topologie/views.py:927 topologie/views.py:988 topologie/views.py:1052 -#: topologie/views.py:1112 topologie/views.py:1161 topologie/views.py:1217 +#: topologie/views.py:378 topologie/views.py:429 topologie/views.py:486 +#: topologie/views.py:779 topologie/views.py:835 topologie/views.py:895 +#: topologie/views.py:955 topologie/views.py:1013 topologie/views.py:1075 +#: topologie/views.py:1135 topologie/views.py:1184 topologie/views.py:1240 msgid "Edit" msgstr "Modifier" -#: topologie/views.py:364 topologie/views.py:556 +#: topologie/views.py:389 topologie/views.py:584 msgid "Nonexistent switch." msgstr "Commutateur réseau inexistant." -#: topologie/views.py:372 +#: topologie/views.py:397 msgid "The port was added." msgstr "Le port a été ajouté." -#: topologie/views.py:374 +#: topologie/views.py:399 msgid "The port already exists." msgstr "Le port existe déjà." -#: topologie/views.py:377 topologie/views.py:445 topologie/views.py:733 -#: topologie/views.py:787 topologie/views.py:848 topologie/views.py:909 -#: topologie/views.py:970 topologie/views.py:1032 topologie/views.py:1095 -#: topologie/views.py:1146 topologie/views.py:1200 +#: topologie/views.py:402 topologie/views.py:471 topologie/views.py:764 +#: topologie/views.py:818 topologie/views.py:878 topologie/views.py:938 +#: topologie/views.py:996 topologie/views.py:1056 topologie/views.py:1118 +#: topologie/views.py:1169 topologie/views.py:1223 msgid "Add" msgstr "Ajouter" -#: topologie/views.py:393 +#: topologie/views.py:419 msgid "The port was edited." msgstr "Le port a été modifié." -#: topologie/views.py:417 +#: topologie/views.py:443 msgid "The port was deleted." msgstr "Le port a été supprimé." -#: topologie/views.py:423 +#: topologie/views.py:449 #, python-format msgid "The port %s is used by another object, impossible to delete it." msgstr "Le port %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:442 +#: topologie/views.py:468 msgid "The stack was created." msgstr "La pile a été créée." -#: topologie/views.py:457 +#: topologie/views.py:483 msgid "The stack was edited." msgstr "La pile a été modifiée." -#: topologie/views.py:471 +#: topologie/views.py:497 msgid "The stack was deleted." msgstr "La pile a été supprimée." -#: topologie/views.py:477 +#: topologie/views.py:503 #, python-format msgid "The stack %s is used by another object, impossible to deleted it." msgstr "" "La pile %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:516 topologie/views.py:638 topologie/views.py:692 +#: topologie/views.py:544 topologie/views.py:670 topologie/views.py:723 msgid "" "The organisation's user doesn't exist yet, please create or link it in the " "preferences." @@ -927,131 +919,131 @@ msgstr "" "L'utilisateur de l'association n'existe pas encore, veuillez le créer ou le " "relier dans les préférences." -#: topologie/views.py:533 +#: topologie/views.py:561 msgid "The switch was created." msgstr "Le commutateur réseau a été créé." -#: topologie/views.py:569 +#: topologie/views.py:597 msgid "The ports were created." msgstr "Les ports ont été créés." -#: topologie/views.py:605 +#: topologie/views.py:635 msgid "The switch was edited." msgstr "Le commutateur réseau a été modifié." -#: topologie/views.py:655 +#: topologie/views.py:687 msgid "The access point was created." msgstr "Le point d'accès sans fil a été créé." -#: topologie/views.py:707 +#: topologie/views.py:738 msgid "The access point was edited." msgstr "Le point d'accès sans fil a été modifié." -#: topologie/views.py:730 +#: topologie/views.py:761 msgid "The room was created." msgstr "La chambre a été créée." -#: topologie/views.py:745 +#: topologie/views.py:776 msgid "The room was edited." msgstr "La chambre a été modifiée." -#: topologie/views.py:759 +#: topologie/views.py:790 msgid "The room was deleted." msgstr "La chambre a été supprimée." -#: topologie/views.py:765 +#: topologie/views.py:796 #, python-format msgid "The room %s is used by another object, impossible to deleted it." msgstr "" "La chambre %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:784 +#: topologie/views.py:815 msgid "The switch model was created." msgstr "Le modèle de commutateur réseau a été créé." -#: topologie/views.py:802 +#: topologie/views.py:832 msgid "The switch model was edited." msgstr "Le modèle de commutateur réseau a été modifié." -#: topologie/views.py:818 +#: topologie/views.py:848 msgid "The switch model was deleted." msgstr "Le modèle de commutateur réseau a été supprimé." -#: topologie/views.py:824 +#: topologie/views.py:854 #, python-format msgid "The switch model %s is used by another object, impossible to delete it." msgstr "" "Le modèle de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: topologie/views.py:845 +#: topologie/views.py:875 msgid "The switch bay was created." msgstr "La baie de brassage a été créée." -#: topologie/views.py:862 +#: topologie/views.py:892 msgid "The switch bay was edited." msgstr "La baie de brassage a été modifiée." -#: topologie/views.py:878 +#: topologie/views.py:908 msgid "The switch bay was deleted." msgstr "La baie de brassage a été supprimée." -#: topologie/views.py:884 +#: topologie/views.py:914 #, python-format msgid "The switch bay %s is used by another object, impossible to delete it." msgstr "" "La baie de brassage %s est utilisée par un autre objet, impossible de la " "supprimer." -#: topologie/views.py:906 +#: topologie/views.py:935 msgid "The building was created." msgstr "Le bâtiment a été créé." -#: topologie/views.py:924 +#: topologie/views.py:952 msgid "The building was edited." msgstr "Le bâtiment a été modifié." -#: topologie/views.py:939 +#: topologie/views.py:966 msgid "The building was deleted." msgstr "Le bâtiment a été supprimé." -#: topologie/views.py:945 +#: topologie/views.py:972 #, python-format msgid "The building %s is used by another object, impossible to delete it." msgstr "" "Le bâtiment %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:967 +#: topologie/views.py:993 msgid "The dormitory was created." msgstr "La résidence a été créée." -#: topologie/views.py:985 +#: topologie/views.py:1010 msgid "The dormitory was edited." msgstr "La résidence a été modifiée." -#: topologie/views.py:1002 +#: topologie/views.py:1026 msgid "The dormitory was deleted." msgstr "La résidence a été supprimée." -#: topologie/views.py:1008 +#: topologie/views.py:1032 #, python-format msgid "The dormitory %s is used by another object, impossible to delete it." msgstr "" "La résidence %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:1029 +#: topologie/views.py:1053 msgid "The switch constructor was created." msgstr "Le constructeur de commutateur réseau a été créé." -#: topologie/views.py:1049 +#: topologie/views.py:1072 msgid "The switch constructor was edited." msgstr "Le constructeur de commutateur réseau a été modifié." -#: topologie/views.py:1065 +#: topologie/views.py:1088 msgid "The switch constructor was deleted." msgstr "Le constructeur de commutateur réseau a été supprimé." -#: topologie/views.py:1071 +#: topologie/views.py:1094 #, python-format msgid "" "The switch constructor %s is used by another object, impossible to delete it." @@ -1059,52 +1051,61 @@ msgstr "" "Le constructeur de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: topologie/views.py:1092 +#: topologie/views.py:1115 msgid "The port profile was created." msgstr "Le profil de port a été créé." -#: topologie/views.py:1109 +#: topologie/views.py:1132 msgid "The port profile was edited." msgstr "Le profil de port a été modifié." -#: topologie/views.py:1125 +#: topologie/views.py:1148 msgid "The port profile was deleted." msgstr "Le profil de port a été supprimé." -#: topologie/views.py:1127 +#: topologie/views.py:1150 msgid "Impossible to delete the port profile." msgstr "Impossible de supprimer le profil de port." -#: topologie/views.py:1143 +#: topologie/views.py:1166 msgid "The module was created." msgstr "Le module a été créé." -#: topologie/views.py:1158 topologie/views.py:1214 +#: topologie/views.py:1181 topologie/views.py:1237 msgid "The module was edited." msgstr "Le module a été modifié." -#: topologie/views.py:1172 topologie/views.py:1228 +#: topologie/views.py:1195 topologie/views.py:1251 msgid "The module was deleted." msgstr "Le module a été supprimé." -#: topologie/views.py:1178 topologie/views.py:1234 +#: topologie/views.py:1201 topologie/views.py:1257 #, python-format msgid "The module %s is used by another object, impossible to deleted it." msgstr "" "Le module %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:1186 topologie/views.py:1242 +#: topologie/views.py:1209 topologie/views.py:1265 msgid "module" msgstr "module" -#: topologie/views.py:1197 +#: topologie/views.py:1220 msgid "The module was added." msgstr "Le module a été ajouté." -#: topologie/views.py:1408 +#: topologie/views.py:1434 msgid "" "The default Django template isn't used. This can lead to rendering errors. " "Check the parameters." msgstr "" "Le gabarit par défaut de Django n'est pas utilisé. Cela peut entraîner des " "erreurs de rendu. Vérifiez les paramètres." + +#~ msgid "Rooms and premises" +#~ msgstr "Chambres et locaux" + +#~ msgid "Physical grouping" +#~ msgstr "Groupements physiques" + +#~ msgid "Switch models and constructors" +#~ msgstr "Modèles et constructeurs de commutateur réseau" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 6c950d10..2e317b0a 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-05-17 12:49+0200\n" +"POT-Creation-Date: 2020-11-17 21:07+0100\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" @@ -35,128 +35,112 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: users/forms.py:81 -msgid "Current password" -msgstr "Mot de passe actuel" - -#: users/forms.py:84 users/forms.py:603 users/forms.py:631 -msgid "New password" -msgstr "Nouveau mot de passe" - -#: users/forms.py:90 -msgid "New password confirmation" -msgstr "Confirmation du nouveau mot de passe" - -#: users/forms.py:105 -msgid "The new passwords don't match." -msgstr "Les nouveaux mots de passe ne correspondent pas." - -#: users/forms.py:112 -msgid "The current password is incorrect." -msgstr "Le mot de passe actuel est incorrect." - -#: users/forms.py:133 users/forms.py:181 users/forms.py:407 -#: users/models.py:1975 +#: users/forms.py:96 users/forms.py:159 users/forms.py:438 users/models.py:2728 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:139 users/forms.py:184 users/forms.py:414 +#: users/forms.py:103 users/forms.py:164 users/forms.py:445 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: users/forms.py:143 users/forms.py:224 -msgid "Is admin" -msgstr "Est admin" - -#: users/forms.py:160 users/forms.py:204 users/forms.py:477 +#: users/forms.py:130 users/forms.py:189 users/forms.py:515 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: users/forms.py:233 -#, python-format -msgid "User is admin: %s" -msgstr "L'utilisateur est admin : %s" +#: users/forms.py:219 +msgid "Current password" +msgstr "Mot de passe actuel" -#: users/forms.py:279 users/templates/users/aff_clubs.html:38 +#: users/forms.py:222 users/forms.py:671 users/forms.py:710 +msgid "New password" +msgstr "Nouveau mot de passe" + +#: users/forms.py:228 +msgid "New password confirmation" +msgstr "Confirmation du nouveau mot de passe" + +#: users/forms.py:250 +msgid "The new passwords don't match." +msgstr "Les nouveaux mots de passe ne correspondent pas." + +#: users/forms.py:262 +msgid "The current password is incorrect." +msgstr "Le mot de passe actuel est incorrect." + +#: users/forms.py:285 users/templates/users/aff_clubs.html:38 #: users/templates/users/aff_listright.html:63 #: users/templates/users/aff_listright.html:168 #: users/templates/users/aff_users.html:39 -#: users/templates/users/profil.html:204 users/templates/users/profil.html:375 -#: users/templates/users/profil.html:394 +#: users/templates/users/profil.html:208 users/templates/users/profil.html:383 +#: users/templates/users/profil.html:402 msgid "Username" msgstr "Pseudo" -#: users/forms.py:291 +#: users/forms.py:300 msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" msgstr "" "Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" -#: users/forms.py:304 +#: users/forms.py:313 msgid "Impossible to archive users whose end access date is in the future." msgstr "" "Impossible d'archiver des utilisateurs dont la date de fin de connexion est " "dans le futur." -#: users/forms.py:319 users/templates/users/aff_users.html:35 -#: users/templates/users/profil.html:193 users/templates/users/profil.html:374 -#: users/templates/users/profil.html:393 +#: users/forms.py:331 users/templates/users/aff_users.html:35 +#: users/templates/users/profil.html:197 users/templates/users/profil.html:382 +#: users/templates/users/profil.html:401 msgid "First name" msgstr "Prénom" -#: users/forms.py:320 users/templates/users/aff_users.html:37 -#: users/templates/users/profil.html:199 users/templates/users/profil.html:373 -#: users/templates/users/profil.html:392 +#: users/forms.py:332 users/templates/users/aff_users.html:37 +#: users/templates/users/profil.html:203 users/templates/users/profil.html:381 +#: users/templates/users/profil.html:400 msgid "Surname" msgstr "Nom" -#: users/forms.py:321 users/forms.py:540 users/models.py:1975 +#: users/forms.py:333 users/forms.py:591 users/models.py:2728 #: users/templates/users/aff_emailaddress.html:36 -#: users/templates/users/profil.html:216 +#: users/templates/users/profil.html:220 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:322 users/forms.py:538 users/forms.py:684 +#: users/forms.py:334 users/forms.py:589 users/forms.py:776 #: users/templates/users/aff_schools.html:37 -#: users/templates/users/profil.html:237 +#: users/templates/users/profil.html:241 msgid "School" msgstr "Établissement" -#: users/forms.py:323 users/forms.py:539 +#: users/forms.py:335 users/forms.py:590 #: users/templates/users/aff_serviceusers.html:34 -#: users/templates/users/profil.html:242 +#: users/templates/users/profil.html:246 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:324 -#, fuzzy -#| msgid "Profile" -msgid "Profile Image" -msgstr "Photo de profil" - -#: users/forms.py:326 users/forms.py:542 +#: users/forms.py:337 users/forms.py:593 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 -#: users/templates/users/profil.html:221 +#: users/templates/users/profil.html:225 msgid "Room" msgstr "Chambre" -#: users/forms.py:327 users/forms.py:543 +#: users/forms.py:338 users/forms.py:594 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:328 users/forms.py:544 +#: users/forms.py:339 users/forms.py:595 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: users/forms.py:345 +#: users/forms.py:355 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:353 users/forms.py:568 +#: users/forms.py:370 users/forms.py:625 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:387 +#: users/forms.py:418 msgid "" "If this options is set, you will receive a link to set your initial password " "by email. If you do not have any means of accessing your emails, you can " @@ -171,11 +155,11 @@ msgstr "" "votre adresse dans les délais impartis, votre connexion sera automatiquement " "suspendue." -#: users/forms.py:401 +#: users/forms.py:432 msgid "Send password reset link by email." msgstr "Envoyer le lien de modification du mot de passe par mail." -#: users/forms.py:421 +#: users/forms.py:452 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -186,271 +170,271 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:428 +#: users/forms.py:459 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:455 +#: users/forms.py:485 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:457 +#: users/forms.py:487 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:504 +#: users/forms.py:552 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: users/forms.py:509 +#: users/forms.py:557 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: users/forms.py:537 users/templates/users/aff_clubs.html:36 +#: users/forms.py:588 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:545 +#: users/forms.py:596 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:652 users/templates/users/profil.html:284 +#: users/forms.py:736 users/templates/users/profil.html:288 msgid "State" msgstr "État" -#: users/forms.py:653 +#: users/forms.py:737 msgid "Email state" msgstr "État du mail" -#: users/forms.py:671 users/templates/users/aff_listright.html:38 +#: users/forms.py:759 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:697 +#: users/forms.py:793 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:717 +#: users/forms.py:818 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:729 +#: users/forms.py:835 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:738 +#: users/forms.py:849 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:756 +#: users/forms.py:872 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:775 users/forms.py:790 users/templates/users/aff_bans.html:41 +#: users/forms.py:895 users/forms.py:914 users/templates/users/aff_bans.html:41 #: users/templates/users/aff_whitelists.html:41 msgid "End date" msgstr "Date de fin" -#: users/forms.py:805 +#: users/forms.py:934 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:806 +#: users/forms.py:935 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:823 +#: users/forms.py:956 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:826 +#: users/forms.py:959 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:828 +#: users/forms.py:961 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:868 +#: users/forms.py:1007 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:873 +#: users/forms.py:1012 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" -#: users/models.py:111 +#: users/models.py:130 #, python-format msgid "The username \"%(label)s\" contains forbidden characters." msgstr "Le pseudo « %(label)s » contient des caractères interdits." -#: users/models.py:140 +#: users/models.py:167 msgid "Users must have an username." msgstr "Les utilisateurs doivent avoir un pseudo." -#: users/models.py:143 +#: users/models.py:170 msgid "Username should only contain [a-z0-9-]." msgstr "Le pseudo devrait seulement contenir [a-z0-9-]" -#: users/models.py:187 users/templates/users/aff_clubs.html:55 +#: users/models.py:232 users/templates/users/aff_clubs.html:55 #: users/templates/users/aff_users.html:57 -#: users/templates/users/profil.html:286 +#: users/templates/users/profil.html:290 msgid "Active" msgstr "Actif" -#: users/models.py:188 users/templates/users/aff_clubs.html:57 +#: users/models.py:233 users/templates/users/aff_clubs.html:57 #: users/templates/users/aff_users.html:59 -#: users/templates/users/profil.html:288 users/templates/users/profil.html:304 +#: users/templates/users/profil.html:292 users/templates/users/profil.html:308 msgid "Disabled" msgstr "Désactivé" -#: users/models.py:189 users/templates/users/profil.html:290 +#: users/models.py:234 users/templates/users/profil.html:294 msgid "Archived" msgstr "Archivé" -#: users/models.py:190 users/templates/users/profil.html:292 +#: users/models.py:235 users/templates/users/profil.html:296 msgid "Not yet active" msgstr "Pas encore adhéré" -#: users/models.py:191 users/templates/users/profil.html:294 +#: users/models.py:236 users/templates/users/profil.html:298 msgid "Fully archived" msgstr "Complètement archivé" -#: users/models.py:198 +#: users/models.py:243 msgid "Confirmed" msgstr "Confirmé" -#: users/models.py:199 +#: users/models.py:244 msgid "Not confirmed" msgstr "Non confirmé" -#: users/models.py:200 +#: users/models.py:245 msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:207 users/models.py:1628 +#: users/models.py:252 users/models.py:2237 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." -#: users/models.py:213 +#: users/models.py:258 msgid "External email address allowing us to contact you." msgstr "Adresse mail externe nous permettant de vous contacter." -#: users/models.py:218 +#: users/models.py:263 msgid "" "Enable redirection of the local email messages to the main email address." msgstr "" "Activer la redirection des mails locaux vers l'adresse mail principale." -#: users/models.py:224 +#: users/models.py:269 msgid "Enable the local email account." msgstr "Activer le compte mail local" -#: users/models.py:231 +#: users/models.py:276 msgid "Education institute." msgstr "Etablissement d'enseignement" -#: users/models.py:238 +#: users/models.py:283 msgid "Unix shell." msgstr "Interface en ligne de commande" -#: users/models.py:241 +#: users/models.py:286 msgid "Comment, school year." msgstr "Commentaire, promotion." -#: users/models.py:247 +#: users/models.py:292 msgid "Account state." msgstr "Etat du compte" -#: users/models.py:257 +#: users/models.py:302 msgid "Optionnal legacy uid, for import and transition purpose" msgstr "Uid legacy optionnel, pour aider les imports et transitions" -#: users/models.py:260 +#: users/models.py:305 msgid "enable shortcuts on Re2o website" msgstr "activer les raccourcis sur le site de Re2o" -#: users/models.py:273 +#: users/models.py:318 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: users/models.py:274 +#: users/models.py:319 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: users/models.py:275 +#: users/models.py:320 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: users/models.py:276 +#: users/models.py:321 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:277 +#: users/models.py:322 msgid "Can edit the pseudo of a user" msgstr "Peut changer le pseudo d'un utilisateur" -#: users/models.py:280 +#: users/models.py:325 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:282 +#: users/models.py:327 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:283 +#: users/models.py:328 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:285 +#: users/models.py:330 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:286 +#: users/models.py:331 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:304 users/models.py:332 users/models.py:342 +#: users/models.py:367 users/models.py:420 users/models.py:438 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:338 users/templates/users/aff_listright.html:75 +#: users/models.py:434 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:340 +#: users/models.py:436 msgid "Club" msgstr "Club" -#: users/models.py:919 +#: users/models.py:1452 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:921 +#: users/models.py:1454 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:944 users/templates/users/user_autocapture.html:64 +#: users/models.py:1477 users/templates/users/user_autocapture.html:64 msgid "OK" msgstr "OK" -#: users/models.py:1042 +#: users/models.py:1572 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:1056 users/models.py:1110 +#: users/models.py:1586 users/models.py:1640 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:1068 +#: users/models.py:1598 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:1075 +#: users/models.py:1605 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -458,261 +442,261 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:1087 users/models.py:1125 +#: users/models.py:1617 users/models.py:1655 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:1151 +#: users/models.py:1681 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1168 +#: users/models.py:1698 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1188 +#: users/models.py:1718 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1210 +#: users/models.py:1741 msgid "You don't have the right to change the pseudo." msgstr "Vous n'avez pas le droit de changer le pseudo." -#: users/models.py:1227 users/models.py:1242 +#: users/models.py:1758 users/models.py:1773 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1257 +#: users/models.py:1788 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1272 +#: users/models.py:1803 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1288 +#: users/models.py:1820 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1313 +#: users/models.py:1846 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1322 +#: users/models.py:1855 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1337 users/models.py:1564 +#: users/models.py:1871 users/models.py:2152 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1354 +#: users/models.py:1888 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1376 +#: users/models.py:1920 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1383 +#: users/models.py:1940 msgid "Email field cannot be empty." msgstr "Le champ mail ne peut pas ^êêtre vide" -#: users/models.py:1390 +#: users/models.py:1947 msgid "You can't use a {} address as an external contact address." msgstr "Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." -#: users/models.py:1434 +#: users/models.py:1993 msgid "member" msgstr "adhérent" -#: users/models.py:1435 +#: users/models.py:1994 msgid "members" msgstr "adhérents" -#: users/models.py:1452 +#: users/models.py:2025 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:1477 +#: users/models.py:2052 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:1487 +#: users/models.py:2062 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:1517 +#: users/models.py:2105 msgid "club" msgstr "club" -#: users/models.py:1518 +#: users/models.py:2106 msgid "clubs" msgstr "clubs" -#: users/models.py:1529 +#: users/models.py:2117 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:1537 +#: users/models.py:2125 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:1632 +#: users/models.py:2241 msgid "Comment." msgstr "Commentaire." -#: users/models.py:1638 +#: users/models.py:2247 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:1639 users/views.py:349 +#: users/models.py:2248 users/views.py:496 msgid "service user" msgstr "utilisateur service" -#: users/models.py:1640 +#: users/models.py:2249 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:1644 +#: users/models.py:2258 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:1711 +#: users/models.py:2363 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:1712 +#: users/models.py:2364 msgid "school" msgstr "établissement" -#: users/models.py:1713 +#: users/models.py:2365 msgid "schools" msgstr "établissements" -#: users/models.py:1732 +#: users/models.py:2392 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:1738 +#: users/models.py:2398 msgid "Description." msgstr "Description." -#: users/models.py:1741 +#: users/models.py:2401 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:1742 +#: users/models.py:2402 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:1743 +#: users/models.py:2403 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:1788 +#: users/models.py:2474 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:1789 users/views.py:649 +#: users/models.py:2475 users/views.py:963 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:1790 +#: users/models.py:2476 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:1808 +#: users/models.py:2511 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:1809 +#: users/models.py:2512 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:1810 +#: users/models.py:2513 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:1821 +#: users/models.py:2524 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:1822 users/views.py:400 +#: users/models.py:2525 users/views.py:578 msgid "ban" msgstr "bannissement" -#: users/models.py:1823 +#: users/models.py:2526 msgid "bans" msgstr "bannissements" -#: users/models.py:1860 +#: users/models.py:2580 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:1908 +#: users/models.py:2641 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:1909 +#: users/models.py:2642 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1910 +#: users/models.py:2643 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:1930 +#: users/models.py:2671 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2128 +#: users/models.py:2756 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2131 +#: users/models.py:2759 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2136 +#: users/models.py:2764 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2138 +#: users/models.py:2766 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2139 +#: users/models.py:2767 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2167 users/models.py:2202 users/models.py:2236 -#: users/models.py:2270 +#: users/models.py:2805 users/models.py:2840 users/models.py:2874 +#: users/models.py:2908 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2172 +#: users/models.py:2810 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2182 +#: users/models.py:2820 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2208 +#: users/models.py:2846 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2228 +#: users/models.py:2866 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -720,13 +704,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2242 +#: users/models.py:2880 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2262 +#: users/models.py:2900 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -734,13 +718,13 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2276 +#: users/models.py:2914 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2285 +#: users/models.py:2934 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." @@ -766,7 +750,7 @@ msgstr "Fin de cotisation le" #: users/templates/users/aff_clubs.html:43 #: users/templates/users/aff_users.html:44 -#: users/templates/users/profil.html:299 +#: users/templates/users/profil.html:303 msgid "Internet access" msgstr "Accès Internet" @@ -777,7 +761,7 @@ msgstr "Profil" #: users/templates/users/aff_clubs.html:53 #: users/templates/users/aff_users.html:54 -#: users/templates/users/profil.html:261 +#: users/templates/users/profil.html:265 msgid "Not a member" msgstr "Non adhérent" @@ -860,8 +844,7 @@ msgstr[1] "Total: %(perm_count)s permissions" #: users/templates/users/index_serviceusers.html:30 #: users/templates/users/index_shell.html:30 #: users/templates/users/index_whitelist.html:29 -#: users/templates/users/plugin_out.html:31 -#: users/templates/users/sidebar.html:52 users/templates/users/user.html:30 +#: users/templates/users/plugin_out.html:31 users/templates/users/user.html:30 #: users/templates/users/user_autocapture.html:30 msgid "Users" msgstr "Utilisateurs" @@ -876,7 +859,7 @@ msgid "Access group" msgstr "Group d'accès" #: users/templates/users/aff_shell.html:32 -#: users/templates/users/profil.html:340 +#: users/templates/users/profil.html:344 msgid "Shell" msgstr "Interface en ligne de commande" @@ -898,8 +881,8 @@ msgstr "pour %(name)s." #: users/templates/users/confirm_email.html:39 #: users/templates/users/delete.html:36 #: users/templates/users/mass_archive.html:36 -#: users/templates/users/resend_confirmation_email.html:35 users/views.py:607 -#: users/views.py:716 +#: users/templates/users/resend_confirmation_email.html:35 users/views.py:887 +#: users/views.py:1062 msgid "Confirm" msgstr "Confirmer" @@ -917,7 +900,7 @@ msgstr "" "( %(objet)s ) ?" #: users/templates/users/index_ban.html:32 -#: users/templates/users/profil.html:471 users/templates/users/sidebar.html:58 +#: users/templates/users/profil.html:428 msgid "Bans" msgstr "Bannissements" @@ -988,7 +971,7 @@ msgid "Add a shell" msgstr "Ajouter une interface en ligne de commande" #: users/templates/users/index_whitelist.html:32 -#: users/templates/users/profil.html:496 users/templates/users/sidebar.html:64 +#: users/templates/users/profil.html:453 msgid "Whitelists" msgstr "Accès gracieux" @@ -1079,16 +1062,15 @@ msgstr "Étendre la durée de connexion" msgid "Refill the balance" msgstr "Recharger le solde" -#: users/templates/users/profil.html:123 users/templates/users/profil.html:415 +#: users/templates/users/profil.html:123 msgid "Machines" msgstr "Machines" #: users/templates/users/profil.html:127 users/templates/users/profil.html:139 -#: users/templates/users/profil.html:423 msgid "Add a machine" msgstr "Ajouter une machine" -#: users/templates/users/profil.html:135 users/templates/users/profil.html:430 +#: users/templates/users/profil.html:135 msgid "No machine" msgstr "Pas de machine" @@ -1096,14 +1078,14 @@ msgstr "Pas de machine" msgid "Detailed information" msgstr "Informations détaillées" -#: users/templates/users/profil.html:161 users/views.py:204 users/views.py:234 -#: users/views.py:253 users/views.py:268 users/views.py:336 users/views.py:389 -#: users/views.py:440 users/views.py:496 users/views.py:544 users/views.py:578 -#: users/views.py:636 users/views.py:684 +#: users/templates/users/profil.html:161 users/views.py:250 users/views.py:289 +#: users/views.py:319 users/views.py:345 users/views.py:471 users/views.py:556 +#: users/views.py:636 users/views.py:725 users/views.py:795 users/views.py:848 +#: users/views.py:937 users/views.py:1018 users/views.py:1619 msgid "Edit" msgstr "Modifier" -#: users/templates/users/profil.html:165 users/views.py:285 users/views.py:1019 +#: users/templates/users/profil.html:165 users/views.py:373 users/views.py:1480 msgid "Change the password" msgstr "Changer le mot de passe" @@ -1115,158 +1097,148 @@ msgstr "Changer l'état" msgid "Edit the groups" msgstr "Modifier les groupes" -#: users/templates/users/profil.html:186 +#: users/templates/users/profil.html:181 +msgid "Change theme" +msgstr "Changer le thème" + +#: users/templates/users/profil.html:190 msgid "Mailing" msgstr "Envoi de mails" -#: users/templates/users/profil.html:190 +#: users/templates/users/profil.html:194 msgid "Mailing disabled" msgstr "Envoi de mails désactivé" -#: users/templates/users/profil.html:210 -#, fuzzy -#| msgid "Profile" +#: users/templates/users/profil.html:214 msgid "Profile picture" -msgstr "Profil" +msgstr "Photo de profil" -#: users/templates/users/profil.html:217 +#: users/templates/users/profil.html:221 msgid "Pending confirmation..." msgstr "En attente de confirmation..." -#: users/templates/users/profil.html:225 +#: users/templates/users/profil.html:229 msgid "Connected" msgstr "Connecté" -#: users/templates/users/profil.html:226 +#: users/templates/users/profil.html:230 msgid "Pending connection..." msgstr "Connexion en attente..." -#: users/templates/users/profil.html:232 +#: users/templates/users/profil.html:236 msgid "Telephone number" msgstr "Numéro de téléphone" -#: users/templates/users/profil.html:247 +#: users/templates/users/profil.html:251 msgid "Registration date" msgstr "Date d'inscription" -#: users/templates/users/profil.html:252 +#: users/templates/users/profil.html:256 msgid "Last login" msgstr "Dernière connexion" -#: users/templates/users/profil.html:257 +#: users/templates/users/profil.html:261 msgid "End of membership" msgstr "Fin d'adhésion" -#: users/templates/users/profil.html:266 +#: users/templates/users/profil.html:270 msgid "Whitelist" msgstr "Accès gracieux" -#: users/templates/users/profil.html:270 users/templates/users/profil.html:313 +#: users/templates/users/profil.html:274 users/templates/users/profil.html:317 msgid "None" msgstr "Aucun" -#: users/templates/users/profil.html:275 +#: users/templates/users/profil.html:279 msgid "Ban" msgstr "Bannissement" -#: users/templates/users/profil.html:279 +#: users/templates/users/profil.html:283 msgid "Not banned" msgstr "Non banni" -#: users/templates/users/profil.html:302 +#: users/templates/users/profil.html:306 #, python-format msgid "Active (until %(end_access)s)" msgstr "Actif (jusqu'au %(end_access)s)" -#: users/templates/users/profil.html:309 users/templates/users/sidebar.html:82 +#: users/templates/users/profil.html:313 msgid "Groups of rights" msgstr "Groupes de droits" -#: users/templates/users/profil.html:318 +#: users/templates/users/profil.html:322 msgid "Balance" msgstr "Solde" -#: users/templates/users/profil.html:325 +#: users/templates/users/profil.html:329 msgid "Refill" msgstr "Recharger" -#: users/templates/users/profil.html:333 +#: users/templates/users/profil.html:337 msgid "GPG fingerprint" msgstr "Empreinte GPG" -#: users/templates/users/profil.html:345 +#: users/templates/users/profil.html:349 msgid "Shortcuts enabled" msgstr "Raccourcis activés" -#: users/templates/users/profil.html:356 +#: users/templates/users/profil.html:353 +msgid "Theme" +msgstr "Thème" + +#: users/templates/users/profil.html:364 msgid "Manage the club" msgstr "Gérer le club" -#: users/templates/users/profil.html:364 +#: users/templates/users/profil.html:372 msgid "Manage the admins and members" msgstr "Gérer les admins et les membres" -#: users/templates/users/profil.html:368 +#: users/templates/users/profil.html:376 msgid "Club admins" msgstr "Admins du clubs" -#: users/templates/users/profil.html:387 +#: users/templates/users/profil.html:395 msgid "Members" msgstr "Adhérents" -#: users/templates/users/profil.html:440 -msgid "Subscriptions" -msgstr "Cotisations" - -#: users/templates/users/profil.html:448 -msgid "Add a subscription" -msgstr "Ajouter une cotisation" - -#: users/templates/users/profil.html:453 -msgid "Edit the balance" -msgstr "Modifier le solde" - -#: users/templates/users/profil.html:462 -msgid "No invoice" -msgstr "Pas de facture" - -#: users/templates/users/profil.html:479 +#: users/templates/users/profil.html:436 msgid "Add a ban" msgstr "Ajouter un bannissement" -#: users/templates/users/profil.html:487 +#: users/templates/users/profil.html:444 msgid "No ban" msgstr "Pas de bannissement" -#: users/templates/users/profil.html:504 +#: users/templates/users/profil.html:461 msgid "Grant a whitelist" msgstr "Donner un accès gracieux" -#: users/templates/users/profil.html:512 +#: users/templates/users/profil.html:469 msgid "No whitelist" msgstr "Pas d'accès gracieux" -#: users/templates/users/profil.html:520 +#: users/templates/users/profil.html:477 msgid "Email settings" msgstr "Paramètres mail" -#: users/templates/users/profil.html:527 +#: users/templates/users/profil.html:484 msgid "Edit email settings" msgstr "Modifier les paramètres mail" -#: users/templates/users/profil.html:536 users/templates/users/profil.html:562 +#: users/templates/users/profil.html:493 users/templates/users/profil.html:519 msgid "Contact email address" msgstr "Adresse mail de contact" -#: users/templates/users/profil.html:540 +#: users/templates/users/profil.html:497 msgid "Enable the local email account" msgstr "Activer le compte mail local" -#: users/templates/users/profil.html:542 +#: users/templates/users/profil.html:499 msgid "Enable the local email redirection" msgstr "Activer la redirection mail locale" -#: users/templates/users/profil.html:546 +#: users/templates/users/profil.html:503 msgid "" "The contact email address is the email address to which we send emails to " "contact you. If you would like to use your external email address for that, " @@ -1278,7 +1250,7 @@ msgstr "" "externe pour cela, vous pouvez soit désactiver votre adresse mail locale " "soit activer la redirection des mails locaux." -#: users/templates/users/profil.html:551 +#: users/templates/users/profil.html:508 msgid "Add an email address" msgstr "Ajouter une adresse mail" @@ -1290,34 +1262,6 @@ msgstr "Renvoyer l'email de confirmation ?" msgid "The confirmation email will be sent to" msgstr "Le mail de confirmation sera envoyé à" -#: users/templates/users/sidebar.html:33 -msgid "Create a club or organisation" -msgstr "Créer un club ou une association" - -#: users/templates/users/sidebar.html:39 -msgid "Create a user" -msgstr "Créer un utilisateur" - -#: users/templates/users/sidebar.html:46 -msgid "Clubs and organisations" -msgstr "Clubs et associations" - -#: users/templates/users/sidebar.html:70 -msgid "Schools" -msgstr "Établissements" - -#: users/templates/users/sidebar.html:76 -msgid "Shells" -msgstr "Interfaces en ligne de commande" - -#: users/templates/users/sidebar.html:88 -msgid "Service users" -msgstr "Utilisateurs service" - -#: users/templates/users/sidebar.html:94 -msgid "Massively archive" -msgstr "Archiver en masse" - #: users/templates/users/user.html:45 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" @@ -1349,160 +1293,160 @@ msgstr "Connecté avec l'appareil :" msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" -#: users/views.py:136 +#: users/views.py:165 #, python-format msgid "The user %s was created, a confirmation email was sent." msgstr "L'utilisateur %s a été créé, un mail de confirmation a été envoyé." -#: users/views.py:143 +#: users/views.py:172 #, python-format msgid "The user %s was created, an email to set the password was sent." msgstr "" "L'utilisateur %s a été créé, un mail pour initialiser le mot de passe a été " "envoyé." -#: users/views.py:156 +#: users/views.py:185 msgid "Commit" msgstr "Valider" -#: users/views.py:179 +#: users/views.py:217 #, python-format msgid "The club %s was created, an email to set the password was sent." msgstr "" "Le club %s a été créé, un mail pour initialiser le mot de passe a été envoyé." -#: users/views.py:184 +#: users/views.py:222 msgid "Create a club" msgstr "Créer un club" -#: users/views.py:199 +#: users/views.py:245 msgid "The club was edited." msgstr "Le club a été modifié." -#: users/views.py:227 +#: users/views.py:282 msgid "The user was edited." msgstr "L'utilisateur a été modifié." -#: users/views.py:230 +#: users/views.py:285 msgid "Sent a new confirmation email." msgstr "Un nouveau mail de confirmation a été envoyé." -#: users/views.py:246 +#: users/views.py:312 msgid "The states were edited." msgstr "Les états ont été modifié." -#: users/views.py:249 +#: users/views.py:315 msgid "An email to confirm the address was sent." msgstr "Un mail pour confirmer l'adresse a été envoyé." -#: users/views.py:265 +#: users/views.py:342 msgid "The groups were edited." msgstr "Les groupes ont été modifiés." -#: users/views.py:282 users/views.py:1016 +#: users/views.py:370 users/views.py:1477 msgid "The password was changed." msgstr "Le mot de passe a été changé." -#: users/views.py:297 +#: users/views.py:398 #, python-format msgid "%s was removed from the group." msgstr "%s a été retiré du groupe." -#: users/views.py:307 +#: users/views.py:419 #, python-format msgid "%s is no longer superuser." msgstr "%s n'est plus superutilisateur." -#: users/views.py:318 +#: users/views.py:441 msgid "The service user was created." msgstr "L'utilisateur service a été créé." -#: users/views.py:321 users/views.py:372 users/views.py:422 users/views.py:473 -#: users/views.py:562 users/views.py:621 users/views.py:664 +#: users/views.py:444 users/views.py:529 users/views.py:609 users/views.py:691 +#: users/views.py:822 users/views.py:911 users/views.py:988 msgid "Add" msgstr "Ajouter" -#: users/views.py:333 +#: users/views.py:468 msgid "The service user was edited." msgstr "L'utilisateur service a été modifié." -#: users/views.py:346 +#: users/views.py:493 msgid "The service user was deleted." msgstr "L'utilisateur service a été supprimé." -#: users/views.py:368 +#: users/views.py:525 msgid "The ban was added." msgstr "Le bannissement a été ajouté." -#: users/views.py:371 +#: users/views.py:528 msgid "Warning: this user already has an active ban." msgstr "Attention : cet utilisateur a déjà un bannissement actif." -#: users/views.py:387 +#: users/views.py:554 msgid "The ban was edited." msgstr "Le bannissement a été modifié." -#: users/views.py:398 +#: users/views.py:576 msgid "The ban was deleted." msgstr "Le bannissement a été supprimé." -#: users/views.py:415 +#: users/views.py:602 msgid "The whitelist was added." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:419 +#: users/views.py:606 msgid "Warning: this user already has an active whitelist." msgstr "Attention : cet utilisateur a déjà un accès gracieux actif." -#: users/views.py:437 +#: users/views.py:633 msgid "The whitelist was edited." msgstr "L'accès gracieux a été ajouté." -#: users/views.py:450 +#: users/views.py:657 msgid "The whitelist was deleted." msgstr "L'accès gracieux a été supprimé." -#: users/views.py:455 +#: users/views.py:662 msgid "whitelist" msgstr "accès gracieux" -#: users/views.py:470 +#: users/views.py:688 msgid "The local email account was created." msgstr "Le compte mail local a été créé." -#: users/views.py:489 +#: users/views.py:718 msgid "The local email account was edited." msgstr "Le compte mail local a été modifié." -#: users/views.py:508 +#: users/views.py:748 msgid "The local email account was deleted." msgstr "Le compte mail local a été supprimé." -#: users/views.py:513 +#: users/views.py:753 msgid "email address" msgstr "adresse mail" -#: users/views.py:529 +#: users/views.py:780 msgid "The email settings were edited." msgstr "Les paramètres mail ont été modifiés." -#: users/views.py:533 users/views.py:1061 +#: users/views.py:784 users/views.py:1540 msgid "An email to confirm your address was sent." msgstr "Un mail pour confirmer votre adresse a été envoyé." -#: users/views.py:559 +#: users/views.py:819 msgid "The school was added." msgstr "L'établissement a été ajouté." -#: users/views.py:575 +#: users/views.py:845 msgid "The school was edited." msgstr "L'établissement a été modifié." -#: users/views.py:595 +#: users/views.py:875 msgid "The school was deleted." msgstr "L'établissement a été supprimé." -#: users/views.py:600 +#: users/views.py:880 #, python-format msgid "" "The school %s is assigned to at least one user, impossible to delete it." @@ -1510,31 +1454,31 @@ msgstr "" "L'établissement %s est assigné à au moins un utilisateur, impossible de le " "supprimer." -#: users/views.py:618 +#: users/views.py:908 msgid "The shell was added." msgstr "L'interface en ligne de commande a été ajoutée." -#: users/views.py:633 +#: users/views.py:934 msgid "The shell was edited." msgstr "L'interface en ligne de commande a été modifiée." -#: users/views.py:646 +#: users/views.py:960 msgid "The shell was deleted." msgstr "L'interface en ligne de commande a été supprimée." -#: users/views.py:661 +#: users/views.py:985 msgid "The group of rights was added." msgstr "Le groupe de droits a été ajouté." -#: users/views.py:679 +#: users/views.py:1013 msgid "The group of rights was edited." msgstr "Le groupe de droits a été modifié." -#: users/views.py:704 +#: users/views.py:1050 msgid "The group of rights was deleted." msgstr "Le groupe de droits a été supprimé." -#: users/views.py:709 +#: users/views.py:1055 #, python-format msgid "" "The group of rights %s is assigned to at least one user, impossible to " @@ -1543,37 +1487,37 @@ msgstr "" "Le groupe de droits %s est assigné à au moins un utilisateur, impossible de " "le supprimer." -#: users/views.py:745 +#: users/views.py:1101 #, python-format msgid "%s users were archived." msgstr "%s utilisateurs ont été archivés." -#: users/views.py:974 users/views.py:1057 +#: users/views.py:1418 users/views.py:1536 msgid "The user doesn't exist." msgstr "L'utilisateur n'existe pas." -#: users/views.py:976 users/views.py:984 +#: users/views.py:1420 users/views.py:1428 msgid "Reset" msgstr "Réinitialiser" -#: users/views.py:981 +#: users/views.py:1425 msgid "An email to reset the password was sent." msgstr "Un mail pour réinitialiser le mot de passe a été envoyé." -#: users/views.py:999 +#: users/views.py:1452 msgid "Error: please contact an admin." msgstr "Erreur : veuillez contacter un admin." -#: users/views.py:1037 +#: users/views.py:1507 #, python-format msgid "The %s address was confirmed." msgstr "L'adresse mail %s a été confirmée." -#: users/views.py:1080 +#: users/views.py:1573 msgid "Incorrect URL, or already registered device." msgstr "URL incorrect, ou appareil déjà enregistré." -#: users/views.py:1092 +#: users/views.py:1585 msgid "" "Successful registration! Please disconnect and reconnect your Ethernet cable " "to get Internet access." @@ -1581,6 +1525,10 @@ msgstr "" "Enregistrement réussi ! Veuillez débrancher et rebrancher votre câble " "Ethernet pour avoir accès à Internet." +#: users/views.py:1615 +msgid "The theme was edited." +msgstr "Le thème a été modifié." + #: users/widgets.py:39 msgid "Today" msgstr "Aujourd'hui" @@ -1595,27 +1543,4 @@ msgstr "Précédent" #: users/widgets.py:61 msgid "Wk" -msgstr "Sem" - -#~ msgid "You can't use a {} address." -#~ msgstr "Vous ne pouvez pas utiliser une adresse {}." - -#~ msgid "Password must contain at least 8 characters." -#~ msgstr "Le mot de passe doit contenir au moins 8 caractères." - -#~ msgid "" -#~ "There is neither a local email address nor an external email address for " -#~ "this user." -#~ msgstr "" -#~ "Il n'y a pas d'adresse mail locale ni d'adresse mail externe pour cet " -#~ "utilisateur." - -#~ msgid "" -#~ "You can't redirect your local emails if no external email address has " -#~ "been set." -#~ msgstr "" -#~ "Vous ne pouvez pas rediriger vos mails locaux si aucune adresse mail " -#~ "externe n'a été définie." - -#~ msgid "The mailing list doesn't exist." -#~ msgstr "La liste de diffusion n'existe pas." +msgstr "Sem" \ No newline at end of file From 432e531e67ec7479a2ed1af6f0357b841fde582b Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Tue, 17 Nov 2020 22:13:20 +0100 Subject: [PATCH 381/490] Links and translations --- api/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 4 +- logs/locale/fr/LC_MESSAGES/django.po | 2 +- machines/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 3 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 5 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 170 ++++++++------------ tickets/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 3 +- users/locale/fr/LC_MESSAGES/django.po | 22 ++- users/templates/users/index.html | 4 + users/templates/users/index_clubs.html | 4 + 14 files changed, 107 insertions(+), 120 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 797b102a..648f1aaf 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index b31bf830..400171a2 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Yoann Piétri \n" "Language: fr_FR\n" @@ -1111,4 +1111,4 @@ msgstr "Recharger votre solde" #: cotisations/views.py:1048 msgid "Could not find a voucher for that invoice." -msgstr "Impossible de trouver un reçu pour cette facture." \ No newline at end of file +msgstr "Impossible de trouver un reçu pour cette facture." diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 3d7115f4..30536e7a 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index f91dcece..de538913 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index cf070860..43e4e604 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -126,4 +126,3 @@ msgstr "Résidence Activée" #, python-format msgid "The room %s was disconnected." msgstr "La chambre %s a été déconnectée." - diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 4afcf4fd..ae69c93f 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index 80596fc8..d54ea9ec 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -249,6 +249,7 @@ msgid "Contact the organisation %(asso_name)s" msgstr "Contacter l'association %(asso_name)s" #: re2o/templates/re2o/history.html:33 +#, python-format msgid "History of %(title)s" msgstr "Historique de %(title)s" @@ -337,4 +338,4 @@ msgstr "Page d'accueil" #: re2o/views.py:110 re2o/views.py:121 msgid "Unable to get the information." -msgstr "Impossible d'obtenir l'information." \ No newline at end of file +msgstr "Impossible d'obtenir l'information." diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index e44d110b..7e93001b 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index ac667c7b..2dc1dfd3 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,9 +21,9 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" -"Last-Translator: Laouen Fernet \n" +"Last-Translator: Yoann Piétri \n" "Language-Team: \n" "Language: fr_FR\n" "MIME-Version: 1.0\n" @@ -70,15 +70,15 @@ msgstr "Mon compte" #: templates/admin/custom_index.html:29 msgid "Recent actions" -msgstr "" +msgstr "Actions récentes" #: templates/admin/custom_index.html:33 msgid "None available" -msgstr "" +msgstr "Non disponible" #: templates/admin/custom_index.html:48 msgid "Unknown content" -msgstr "" +msgstr "Contenu incconu" #: templates/base.html:79 templates/registration/logged_out.html:11 #: templates/registration/password_change_done.html:11 @@ -88,11 +88,11 @@ msgstr "" #: templates/registration/password_reset_done.html:11 #: templates/registration/password_reset_form.html:11 msgid "Home" -msgstr "" +msgstr "Acceuil" #: templates/buttons/add.html:27 msgid "Add" -msgstr "" +msgstr "Ajouter" #: templates/buttons/edit.html:27 msgid "Edit" @@ -100,7 +100,7 @@ msgstr "Modifier" #: templates/buttons/history.html:27 templates/buttons/history.html:28 msgid "History" -msgstr "" +msgstr "Historique" #: templates/buttons/setlang.html:34 msgid "Translation in development" @@ -116,11 +116,11 @@ msgstr "Tri décroissant" #: templates/buttons/suppr.html:27 msgid "Delete" -msgstr "" +msgstr "Supprimer" #: templates/buttons/view.html:25 msgid "View" -msgstr "" +msgstr "Voir" #: templates/errors/404.html:39 msgid "404 error: page not found" @@ -211,7 +211,7 @@ msgstr "À propos" #: templates/footer.html:42 msgid "Top" -msgstr "" +msgstr "En haut" #: templates/nav.html:45 templates/nav.html:52 templates/nav.html:207 msgid "Users" @@ -219,75 +219,75 @@ msgstr "Utilisateurs" #: templates/nav.html:49 msgid "Users and clubs" -msgstr "" +msgstr "Utilisateurs et Clubs" #: templates/nav.html:55 msgid "Clubs" -msgstr "" +msgstr "Clubs" #: templates/nav.html:58 msgid "Whitelists" -msgstr "" +msgstr "Accès gracieux" #: templates/nav.html:61 msgid "Bans" -msgstr "" +msgstr "Banissements" #: templates/nav.html:64 msgid "Massively archive" -msgstr "" +msgstr "Archivage de masse" #: templates/nav.html:70 msgid "Machines" -msgstr "" +msgstr "Machines" #: templates/nav.html:75 msgid "Groups" -msgstr "" +msgstr "Groupes" #: templates/nav.html:76 templates/nav.html:119 msgid "Advanced" -msgstr "" +msgstr "Avancé" #: templates/nav.html:81 msgid "Schools" -msgstr "" +msgstr "Écoles" #: templates/nav.html:84 msgid "Shells" -msgstr "" +msgstr "Interfaces en ligne de commande" #: templates/nav.html:99 msgid "Treasury" -msgstr "" +msgstr "Trésorerie" #: templates/nav.html:102 templates/nav.html:109 msgid "Invoices" -msgstr "" +msgstr "Factures" #: templates/nav.html:106 msgid "Control invoices" -msgstr "" +msgstr "Contrôler les factures" #: templates/nav.html:112 msgid "Cutsom invoices" -msgstr "" +msgstr "Facture personnalisée" #: templates/nav.html:117 msgid "Cost estimates" -msgstr "" +msgstr "Devis" #: templates/nav.html:124 msgid "Banks" -msgstr "" +msgstr "Banques" #: templates/nav.html:127 msgid "Articles" -msgstr "" +msgstr "Articles" #: templates/nav.html:130 msgid "Payment methods" -msgstr "" +msgstr "Moyens de paiement" #: templates/nav.html:144 msgid "Topology" @@ -298,44 +298,36 @@ msgid "Switches" msgstr "Commutateurs réseau" #: templates/nav.html:154 -#, fuzzy -#| msgid "Switches" msgid "Switch models" -msgstr "Commutateurs réseau" +msgstr "Modèles de commutateurs réseau" #: templates/nav.html:157 -#, fuzzy -#| msgid "Switches" msgid "Switch modules" -msgstr "Commutateurs réseau" +msgstr "Modules de commutateurs réseau" #: templates/nav.html:159 -#, fuzzy -#| msgid "Switches" msgid "Switch bays" -msgstr "Commutateurs réseau" +msgstr "Baies de commutateurs réseau" #: templates/nav.html:161 msgid "Stacks" -msgstr "" +msgstr "Pile de commutateurs réseau" #: templates/nav.html:164 -#, fuzzy -#| msgid "My profile" msgid "Port profiles" -msgstr "Mon profil" +msgstr "Profils de port" #: templates/nav.html:168 msgid "Infrastructure" -msgstr "" +msgstr "Infrastructure" #: templates/nav.html:172 msgid "Dormitories" -msgstr "" +msgstr "Résidences" #: templates/nav.html:174 msgid "Buildings" -msgstr "" +msgstr "Bâtiments" #: templates/nav.html:177 msgid "Rooms" @@ -351,71 +343,71 @@ msgstr "Statistiques" #: templates/nav.html:197 msgid "Summary" -msgstr "" +msgstr "Résumé" #: templates/nav.html:199 msgid "Events" -msgstr "" +msgstr "Évènements" #: templates/nav.html:201 templates/nav.html:226 msgid "General" -msgstr "" +msgstr "Général" #: templates/nav.html:203 msgid "Database" -msgstr "" +msgstr "Base de données" #: templates/nav.html:205 msgid "Wiring actions" -msgstr "" +msgstr "Actions de cablage" #: templates/nav.html:209 msgid "Machine history" -msgstr "" +msgstr "Historique machine" #: templates/nav.html:221 msgid "Administration" -msgstr "" +msgstr "Administration" #: templates/nav.html:231 msgid "LDAP service users" -msgstr "" +msgstr "Utilisateurs service LDAP" #: templates/nav.html:236 msgid "Services" -msgstr "" +msgstr "Services" #: templates/nav.html:239 msgid "Machine types" -msgstr "" +msgstr "Types de machines" #: templates/nav.html:241 msgid "Network" -msgstr "" +msgstr "Réseau" #: templates/nav.html:245 msgid "IP ranges" -msgstr "" +msgstr "Blocs IP" #: templates/nav.html:248 msgid "VLANs" -msgstr "" +msgstr "VLANs" #: templates/nav.html:251 msgid "Extensions and zones" -msgstr "" +msgstr "Zones et extensions" #: templates/nav.html:254 msgid "NAS" -msgstr "" +msgstr "NAS" #: templates/nav.html:257 msgid "Server roles" -msgstr "" +msgstr "Roles de serveurs" #: templates/nav.html:260 msgid "Ports openings" -msgstr "" +msgstr "Ouvertures de ports" #: templates/nav.html:269 msgid "Contact" @@ -428,11 +420,11 @@ msgstr "S'inscrire" #: templates/nav.html:283 templates/registration/login.html:29 #: templates/registration/login.html:36 msgid "Log in" -msgstr "" +msgstr "Se connecter" #: templates/nav.html:291 msgid "Search" -msgstr "" +msgstr "Chercher" #: templates/nav.html:310 msgid "My profile" @@ -440,7 +432,7 @@ msgstr "Mon profil" #: templates/nav.html:312 msgid "Log out" -msgstr "" +msgstr "Se déconnecter" #: templates/pagination.html:35 msgid "First" @@ -460,11 +452,11 @@ msgstr "Dernière page" #: templates/registration/logged_out.html:16 msgid "Thanks for spending some quality time with the Web site today." -msgstr "" +msgstr "Merci pour avoir passé un moment de qualite sur notre site aujourd'hui." #: templates/registration/logged_out.html:17 msgid "Log in again" -msgstr "" +msgstr "Se reconnecter" #: templates/registration/login.html:40 msgid "Forgotten password?" @@ -473,17 +465,17 @@ msgstr "Mot de passe oublié ?" #: templates/registration/password_change_done.html:11 #: templates/registration/password_change_form.html:11 msgid "Password change" -msgstr "" +msgstr "Modification du mot de passe" #: templates/registration/password_reset_complete.html:11 #: templates/registration/password_reset_done.html:11 #: templates/registration/password_reset_form.html:11 msgid "Password reset" -msgstr "" +msgstr "Réinitialisation du mot de passe" #: templates/registration/password_reset_confirm.html:11 msgid "Password reset confirmation" -msgstr "" +msgstr "Confirmation de la réinitialisation du mot de passe" #: templates/registration/password_reset_email.html:2 #, python-format @@ -491,19 +483,21 @@ msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" +"Vous recevez cet email car vous avez demandé une réinitialisation du mot de passe " +"de votre compte sur %(site_name)s." #: templates/registration/password_reset_email.html:4 msgid "Please go to the following page and choose a new password:" -msgstr "" +msgstr "Merci de vous rendre sur la page suivante et de choisir un nouveau mot de passe : " #: templates/registration/password_reset_email.html:9 msgid "Thanks for using our site!" -msgstr "" +msgstr "Merci d'utiliser notre site !" #: templates/registration/password_reset_email.html:11 #, python-format msgid "The %(site_name)s team" -msgstr "" +msgstr "L'équipe de %(site_name)s" #: templates/sidebar.html:34 templates/sidebar.html:74 msgid "Username" @@ -518,8 +512,6 @@ msgid "Internet access" msgstr "Accès Internet" #: templates/sidebar.html:51 templates/sidebar.html:85 -#, fuzzy, python-format -#| msgid "Until %(end_access_date)s" msgid "" "Until\n" " %(end_access_date)s" @@ -534,8 +526,6 @@ msgid "Membership" msgstr "Adhésion" #: templates/sidebar.html:63 templates/sidebar.html:96 -#, fuzzy, python-format -#| msgid "Until %(end_adhesion_date)s" msgid "" "Until\n" " %(end_adhesion_date)s" @@ -554,9 +544,6 @@ msgid "You are not logged in." msgstr "Vous n'êtes pas connecté." #: templates/sidebar.html:120 -#, fuzzy, python-format -#| msgid "%(nb)s active machine" -#| msgid_plural "%(nb)s active machines" msgid "%(nb)s active machine" msgid_plural "" "%(nb)s\n" @@ -566,25 +553,4 @@ msgstr[1] "%(nb)s machines actives" #: templates/sidebar.html:131 msgid "View my machines" -msgstr "Voir mes machines" - -#~ msgid "Manage the users" -#~ msgstr "Gérer les utilisateurs" - -#~ msgid "Manage the clubs" -#~ msgstr "Gérer les clubs" - -#~ msgid "Manage the machines" -#~ msgstr "Gérer les machines" - -#~ msgid "Manage the subscriptions" -#~ msgstr "Gérer les cotisations" - -#~ msgid "Information and contact" -#~ msgstr "Informations et contact" - -#~ msgid "Back to top" -#~ msgstr "Retour en haut" - -#~ msgid "About this website" -#~ msgstr "À propos de ce site" +msgstr "Voir mes machines" \ No newline at end of file diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index 6067d18e..f880f340 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index a8010f37..d9f09aff 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -586,6 +586,7 @@ msgid "MAC address limit" msgstr "Limite d'adresse MAC" #: topologie/templates/topologie/aff_port_profile.html:67 +#, python-format msgid " on %(dorm)s" msgstr " sur %(dorm)s" diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 2e317b0a..5e5474b0 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 21:07+0100\n" +"POT-Creation-Date: 2020-11-17 22:00+0100\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -834,9 +834,9 @@ msgstr[1] "Total: %(perm_count)s permissions" #: users/templates/users/aff_listright.html:150 #: users/templates/users/edit_listright.html:29 -#: users/templates/users/index.html:29 users/templates/users/index.html:32 +#: users/templates/users/index.html:30 users/templates/users/index.html:33 #: users/templates/users/index_ban.html:29 -#: users/templates/users/index_clubs.html:29 +#: users/templates/users/index_clubs.html:30 #: users/templates/users/index_emailaddress.html:29 #: users/templates/users/index_listright.html:30 #: users/templates/users/index_rights.html:29 @@ -899,15 +899,27 @@ msgstr "" "Attention : voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" +#: users/templates/users/index.html:35 +#, fuzzy +#| msgid "Add a shell" +msgid "Add a user" +msgstr "Ajouter une interface en ligne de commande" + #: users/templates/users/index_ban.html:32 #: users/templates/users/profil.html:428 msgid "Bans" msgstr "Bannissements" -#: users/templates/users/index_clubs.html:32 +#: users/templates/users/index_clubs.html:33 msgid "Clubs" msgstr "Clubs" +#: users/templates/users/index_clubs.html:35 +#, fuzzy +#| msgid "Add a ban" +msgid "Add a club" +msgstr "Ajouter un bannissement" + #: users/templates/users/index_emailaddress.html:32 msgid "Local email accounts" msgstr "Comptes mail locaux" @@ -1543,4 +1555,4 @@ msgstr "Précédent" #: users/widgets.py:61 msgid "Wk" -msgstr "Sem" \ No newline at end of file +msgstr "Sem" diff --git a/users/templates/users/index.html b/users/templates/users/index.html index 048ed9ee..81372635 100644 --- a/users/templates/users/index.html +++ b/users/templates/users/index.html @@ -25,11 +25,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} +{% load acl %} {% block title %}{% trans "Users" %}{% endblock %} {% block content %}

    {% trans "Users" %}

    + {% can_create User %} + {% trans "Add a user" %} + {% acl_end %} {% include 'users/aff_users.html' with users_list=users_list %}

    diff --git a/users/templates/users/index_clubs.html b/users/templates/users/index_clubs.html index 73493a19..98591d52 100644 --- a/users/templates/users/index_clubs.html +++ b/users/templates/users/index_clubs.html @@ -25,11 +25,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} +{% load acl %} {% block title %}{% trans "Users" %}{% endblock %} {% block content %}

    {% trans "Clubs" %}

    + {% can_create Club %} + {% trans "Add a club" %} + {% acl_end %} {% include 'users/aff_clubs.html' with clubs_list=clubs_list %}

    From f71bd75606269af319a93fb7a4cf4054a0492b73 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Tue, 17 Nov 2020 22:23:51 +0100 Subject: [PATCH 382/490] Minor typo in translations --- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index f880f340..c1f15f79 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -260,7 +260,7 @@ msgid "" "Warning: are you sure you want to delete this %(objet_name)s object " "( %(objet)s )?" msgstr "" -"Attention: êtes-vous sûr de vouloir supprimer l'objet %(object_name)s " +"Attention: êtes-vous sûr de vouloir supprimer l'objet %(objet_name)s " "( %(objet)s )?" #: tickets/templates/tickets/delete.html:36 From 4b7db9b36eeab177c724679668bf4d725db60eb8 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 18 Nov 2020 02:52:35 +0100 Subject: [PATCH 383/490] Fix has_access global filter on different membership and connexion invoices --- re2o/utils.py | 115 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 34 deletions(-) diff --git a/re2o/utils.py b/re2o/utils.py index 89921db6..7af88a1d 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -75,7 +75,23 @@ def get_group_having_permission(*permission_name): return groups -def all_adherent(search_time=None, including_asso=True): +def filter_results(query_filter, dormitory, user_type): + """Wrapper function. + Take a query filter in main argument + Returns a filtered results : + - on dormitory if specified + - on user_type (adherent or club) is specified + Returns the filter""" + if dormitory: + query_filter &= (Q(adherent__room__building__dormitory=dormitory) | Q(club__room__building__dormitory=dormitory)) + if user_type == "adherent": + query_filter &= Q(adherent__isnull=False) + if user_type == "club": + query_filter &= Q(club__isnull=False) + return query_filter + + +def all_adherent(search_time=None, including_asso=True, dormitory=None, user_type="all"): """Return all people who have a valid membership at org. Optimised to make only one sql query. Build a filter and then apply it to User. Check for each user if a valid membership is registered at the desired search_time. @@ -104,10 +120,11 @@ def all_adherent(search_time=None, including_asso=True): asso_user = AssoOption.get_cached_value("utilisateur_asso") if asso_user: filter_user |= Q(id=asso_user.id) + filter_user = filter_results(filter_user, dormitory, user_type) return User.objects.filter(filter_user).distinct() -def all_baned(search_time=None): +def all_baned(search_time=None, dormitory=None, user_type="all"): """Return all people who are banned at org. Optimised to make only one sql query. Build a filter and then apply it to User. Check for each user banned at the desired search_time. @@ -122,14 +139,16 @@ def all_baned(search_time=None): """ if search_time is None: search_time = timezone.now() - return User.objects.filter( + filter_user = Q( ban__in=Ban.objects.filter( Q(date_start__lt=search_time) & Q(date_end__gt=search_time) ) - ).distinct() + ) + filter_user = filter_results(filter_user, dormitory, user_type) + return User.objects.filter(filter_user).distinct() -def all_whitelisted(search_time=None): +def all_whitelisted(search_time=None, dormitory=None, user_type="all"): """Return all people who have a free access at org. Optimised to make only one sql query. Build a filter and then apply it to User. Check for each user with a whitelisted free access at the desired search_time. @@ -144,16 +163,51 @@ def all_whitelisted(search_time=None): """ if search_time is None: search_time = timezone.now() - return User.objects.filter( + filter_user = Q( whitelist__in=Whitelist.objects.filter( Q(date_start__lt=search_time) & Q(date_end__gt=search_time) ) - ).distinct() + ) + filter_user = filter_results(filter_user, dormitory, user_type) + return User.objects.filter(filter_user).distinct() -def all_has_access(search_time=None, including_asso=True): - """Return all people who have an valid internet access at org. Optimised to make - only one sql query. Build a filter and then apply it to User. Return users +def all_conn(search_time=None, including_asso=True, dormitory=None, user_type="all"): + """Return all people who have a valid connection payment at org. Optimised to make only one + sql query. Build a filter and then apply it to User. Check for each user if a valid + connection is registered at the desired search_time. + + Parameters: + search_time (django datetime): Datetime to perform this search, + if not provided, search_time will be set à timezone.now() + including_asso (boolean): Decide if org itself is included in results + + Returns: + django queryset: Django queryset containing all users with valid connection perdiod + + """ + if search_time is None: + search_time = timezone.now() + filter_user = Q( + facture__in=Facture.objects.filter( + vente__cotisation__in=Cotisation.objects.filter( + Q(vente__facture__facture__valid=True) & + Q(date_start_con__lt=search_time) & + Q(date_end_con__gt=search_time) + ) + ) + ) + if including_asso: + asso_user = AssoOption.get_cached_value("utilisateur_asso") + if asso_user: + filter_user |= Q(id=asso_user.id) + filter_user = filter_results(filter_user, dormitory, user_type) + return User.objects.filter(filter_user).distinct() + + +def all_has_access(search_time=None, including_asso=True, dormitory=None, user_type="all"): + """Return all people who have an valid internet access at org. Call previously buid filters. + Can't do that in one sql query unfortunatly. Apply each filters, and return users with a whitelist, or a valid paid access, except banned users. Parameters: @@ -170,35 +224,28 @@ def all_has_access(search_time=None, including_asso=True): filter_user = ( Q(state=User.STATE_ACTIVE) & ~Q(email_state=User.EMAIL_STATE_UNVERIFIED) - & ~Q( - ban__in=Ban.objects.filter( - Q(date_start__lt=search_time) & Q(date_end__gt=search_time) - ) - ) - & ( - Q( - whitelist__in=Whitelist.objects.filter( - Q(date_start__lt=search_time) & Q(date_end__gt=search_time) - ) - ) - | Q( - facture__in=Facture.objects.filter( - vente__cotisation__in=Cotisation.objects.filter( - Q(vente__facture__facture__valid=True) & - Q(date_start_con__lt=search_time) & - Q(date_end_con__gt=search_time) & - Q(date_start_memb__lt=search_time) & - Q(date_end_memb__gt=search_time) - ) - ) - ) - ) ) + filter_user = filter_results(filter_user, dormitory, user_type) if including_asso: asso_user = AssoOption.get_cached_value("utilisateur_asso") if asso_user: filter_user |= Q(id=asso_user.id) - return User.objects.filter(filter_user).distinct() + filter_user = filter_results(filter_user, dormitory, user_type) + return User.objects.filter( + Q(filter_user) & ( + Q( + id__in=all_whitelisted(search_time=search_time, dormitory=dormitory, user_type=user_type) + ) | ( + Q( + id__in=all_adherent(search_time=search_time, including_asso=including_asso, dormitory=dormitory, user_type=user_type) + ) & Q( + id__in=all_conn(search_time=search_time, including_asso=including_asso, dormitory=dormitory, user_type=user_type) + ) + ) + ) & ~Q( + id__in=all_baned(search_time=search_time, dormitory=dormitory, user_type=user_type) + ) + ).distinct() def filter_active_interfaces(interface_set): From 391f186835213abd489a0eb03368cb06c6afc431 Mon Sep 17 00:00:00 2001 From: Gabriel Detraz Date: Wed, 18 Nov 2020 08:28:45 +0100 Subject: [PATCH 384/490] Remove duplicated line --- re2o/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/re2o/utils.py b/re2o/utils.py index 7af88a1d..00c99df1 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -225,7 +225,6 @@ def all_has_access(search_time=None, including_asso=True, dormitory=None, user_t Q(state=User.STATE_ACTIVE) & ~Q(email_state=User.EMAIL_STATE_UNVERIFIED) ) - filter_user = filter_results(filter_user, dormitory, user_type) if including_asso: asso_user = AssoOption.get_cached_value("utilisateur_asso") if asso_user: From 69f78d8c602655d6d27f6b98b936bf3b3ecf028f Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Mon, 23 Nov 2020 17:06:37 +0100 Subject: [PATCH 385/490] =?UTF-8?q?=C3=87a=20se=20dit=20R=C3=A9zo=20Metz?= =?UTF-8?q?=20'tain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- api/acl.py | 2 +- api/authentication.py | 2 +- api/locale/fr/LC_MESSAGES/django.po | 2 +- api/pagination.py | 2 +- api/permissions.py | 2 +- api/routers.py | 2 +- api/serializers.py | 2 +- api/settings.py | 2 +- api/tests.py | 2 +- api/urls.py | 2 +- api/views.py | 2 +- cotisations/__init__.py | 2 +- cotisations/acl.py | 2 +- cotisations/admin.py | 2 +- cotisations/api/serializers.py | 2 +- cotisations/api/urls.py | 2 +- cotisations/api/views.py | 2 +- cotisations/forms.py | 2 +- cotisations/locale/fr/LC_MESSAGES/django.po | 2 +- cotisations/migrations/0001_initial.py | 2 +- cotisations/migrations/0002_remove_facture_article.py | 2 +- cotisations/migrations/0003_auto_20160702_1448.py | 2 +- cotisations/migrations/0004_auto_20160702_1528.py | 2 +- cotisations/migrations/0005_auto_20160702_1532.py | 2 +- cotisations/migrations/0006_auto_20160702_1534.py | 2 +- cotisations/migrations/0007_auto_20160702_1543.py | 2 +- cotisations/migrations/0008_auto_20160702_1614.py | 2 +- cotisations/migrations/0009_remove_cotisation_user.py | 2 +- cotisations/migrations/0010_auto_20160702_1840.py | 2 +- cotisations/migrations/0011_auto_20160702_1911.py | 2 +- cotisations/migrations/0012_auto_20160704_0118.py | 2 +- cotisations/migrations/0013_auto_20160711_2240.py | 2 +- cotisations/migrations/0014_auto_20160712_0245.py | 2 +- cotisations/migrations/0015_auto_20160714_2142.py | 2 +- cotisations/migrations/0016_auto_20160715_0110.py | 2 +- cotisations/migrations/__init__.py | 2 +- cotisations/models.py | 2 +- cotisations/payment_methods/__init__.py | 2 +- cotisations/payment_methods/balance/__init__.py | 2 +- cotisations/payment_methods/balance/models.py | 2 +- cotisations/payment_methods/cheque/__init__.py | 2 +- cotisations/payment_methods/cheque/forms.py | 2 +- cotisations/payment_methods/cheque/models.py | 2 +- cotisations/payment_methods/cheque/urls.py | 2 +- cotisations/payment_methods/cheque/views.py | 2 +- cotisations/payment_methods/comnpay/__init__.py | 2 +- cotisations/payment_methods/comnpay/models.py | 2 +- cotisations/payment_methods/comnpay/urls.py | 2 +- cotisations/payment_methods/comnpay/views.py | 2 +- cotisations/payment_methods/forms.py | 2 +- cotisations/payment_methods/free/__init__.py | 2 +- cotisations/payment_methods/free/models.py | 2 +- cotisations/payment_methods/mixins.py | 2 +- cotisations/payment_methods/note_kfet/__init__.py | 2 +- cotisations/payment_methods/note_kfet/forms.py | 2 +- cotisations/payment_methods/note_kfet/models.py | 2 +- cotisations/payment_methods/note_kfet/urls.py | 2 +- cotisations/payment_methods/note_kfet/views.py | 2 +- cotisations/payment_methods/urls.py | 2 +- cotisations/templates/cotisations/aff_article.html | 2 +- cotisations/templates/cotisations/aff_banque.html | 2 +- cotisations/templates/cotisations/aff_cost_estimate.html | 2 +- cotisations/templates/cotisations/aff_cotisations.html | 2 +- cotisations/templates/cotisations/aff_custom_invoice.html | 2 +- cotisations/templates/cotisations/aff_paiement.html | 2 +- cotisations/templates/cotisations/aff_profil.html | 2 +- cotisations/templates/cotisations/control.html | 2 +- cotisations/templates/cotisations/delete.html | 2 +- cotisations/templates/cotisations/edit_facture.html | 2 +- cotisations/templates/cotisations/facture.html | 2 +- cotisations/templates/cotisations/index.html | 2 +- cotisations/templates/cotisations/index_article.html | 2 +- cotisations/templates/cotisations/index_banque.html | 2 +- cotisations/templates/cotisations/index_cost_estimate.html | 2 +- cotisations/templates/cotisations/index_custom_invoice.html | 2 +- cotisations/templates/cotisations/index_paiement.html | 2 +- cotisations/templates/cotisations/payment.html | 2 +- cotisations/templates/cotisations/sidebar.html | 2 +- cotisations/tex.py | 2 +- cotisations/urls.py | 2 +- cotisations/utils.py | 2 +- cotisations/views.py | 2 +- freeradius_utils/auth.py | 2 +- logs/__init__.py | 2 +- logs/acl.py | 2 +- logs/forms.py | 2 +- logs/locale/fr/LC_MESSAGES/django.po | 2 +- logs/models.py | 2 +- logs/templates/logs/aff_stats_general.html | 2 +- logs/templates/logs/aff_stats_logs.html | 2 +- logs/templates/logs/aff_stats_models.html | 2 +- logs/templates/logs/aff_stats_users.html | 2 +- logs/templates/logs/aff_summary.html | 2 +- logs/templates/logs/delete.html | 2 +- logs/templates/logs/index.html | 2 +- logs/templates/logs/machine_history.html | 2 +- logs/templates/logs/search_machine_history.html | 2 +- logs/templates/logs/search_stats_logs.html | 2 +- logs/templates/logs/sidebar.html | 2 +- logs/templates/logs/stats_general.html | 2 +- logs/templates/logs/stats_logs.html | 2 +- logs/templates/logs/stats_models.html | 2 +- logs/templates/logs/stats_users.html | 2 +- logs/templatetags/__init__.py | 2 +- logs/templatetags/logs_extra.py | 2 +- logs/tests.py | 2 +- logs/urls.py | 2 +- logs/views.py | 2 +- machines/__init__.py | 2 +- machines/acl.py | 2 +- machines/admin.py | 2 +- machines/api/serializers.py | 2 +- machines/api/urls.py | 2 +- machines/api/views.py | 2 +- machines/forms.py | 2 +- machines/locale/fr/LC_MESSAGES/django.po | 2 +- machines/migrations/0001_initial.py | 2 +- machines/migrations/0002_auto_20160703_1444.py | 2 +- machines/migrations/0003_auto_20160703_1450.py | 2 +- machines/migrations/0004_auto_20160703_1451.py | 2 +- machines/migrations/0005_auto_20160703_1523.py | 2 +- machines/migrations/0006_auto_20160703_1813.py | 2 +- machines/migrations/0007_auto_20160703_1816.py | 2 +- machines/migrations/0008_remove_interface_ipv6.py | 2 +- machines/migrations/0009_auto_20160703_2358.py | 2 +- machines/migrations/0010_auto_20160704_0104.py | 2 +- machines/migrations/0011_auto_20160704_0105.py | 2 +- machines/migrations/0012_auto_20160704_0118.py | 2 +- machines/migrations/0013_auto_20160705_1014.py | 2 +- machines/migrations/0014_auto_20160706_1220.py | 2 +- machines/migrations/0015_auto_20160707_0105.py | 2 +- machines/migrations/0016_auto_20160708_1633.py | 2 +- machines/migrations/0017_auto_20160708_1645.py | 2 +- machines/migrations/0018_auto_20160708_1813.py | 2 +- machines/migrations/0019_auto_20160718_1141.py | 2 +- machines/migrations/0020_auto_20160718_1849.py | 2 +- machines/migrations/0021_auto_20161006_1943.py | 2 +- machines/migrations/0022_auto_20161011_1829.py | 2 +- machines/migrations/0023_iplist_ip_type.py | 2 +- machines/migrations/0024_machinetype_need_infra.py | 2 +- machines/migrations/0025_auto_20161023_0038.py | 2 +- machines/migrations/0026_auto_20161026_1348.py | 2 +- machines/migrations/0027_alias.py | 2 +- machines/migrations/0028_iptype_domaine_ip.py | 2 +- machines/migrations/0029_iptype_domaine_range.py | 2 +- machines/migrations/0030_auto_20161118_1730.py | 2 +- machines/migrations/0031_auto_20161119_1709.py | 2 +- machines/migrations/0032_auto_20161119_1850.py | 2 +- machines/migrations/0033_extension_need_infra.py | 2 +- machines/migrations/0034_iplist_need_infra.py | 2 +- machines/migrations/0035_auto_20161224_1201.py | 2 +- machines/migrations/0036_auto_20161224_1204.py | 2 +- machines/migrations/0037_domain_cname.py | 2 +- machines/migrations/0038_auto_20161224_1721.py | 2 +- machines/migrations/0039_auto_20161224_1732.py | 2 +- machines/migrations/0040_remove_interface_dns.py | 2 +- machines/migrations/0041_remove_ns_interface.py | 2 +- machines/migrations/0042_ns_ns.py | 2 +- machines/migrations/__init__.py | 2 +- machines/models.py | 2 +- machines/templates/machines/aff_alias.html | 2 +- machines/templates/machines/aff_dname.html | 2 +- machines/templates/machines/aff_extension.html | 2 +- machines/templates/machines/aff_iptype.html | 2 +- machines/templates/machines/aff_ipv6.html | 2 +- machines/templates/machines/aff_machines.html | 2 +- machines/templates/machines/aff_machinetype.html | 2 +- machines/templates/machines/aff_mx.html | 2 +- machines/templates/machines/aff_nas.html | 2 +- machines/templates/machines/aff_ns.html | 2 +- machines/templates/machines/aff_portlist.html | 2 +- machines/templates/machines/aff_profil.html | 2 +- machines/templates/machines/aff_role.html | 2 +- machines/templates/machines/aff_servers.html | 2 +- machines/templates/machines/aff_service.html | 2 +- machines/templates/machines/aff_soa.html | 2 +- machines/templates/machines/aff_srv.html | 2 +- machines/templates/machines/aff_sshfp.html | 2 +- machines/templates/machines/aff_txt.html | 2 +- machines/templates/machines/aff_vlan.html | 2 +- machines/templates/machines/delete.html | 2 +- machines/templates/machines/edit_portlist.html | 2 +- machines/templates/machines/index.html | 2 +- machines/templates/machines/index_alias.html | 2 +- machines/templates/machines/index_extension.html | 2 +- machines/templates/machines/index_iptype.html | 2 +- machines/templates/machines/index_ipv6.html | 2 +- machines/templates/machines/index_machinetype.html | 2 +- machines/templates/machines/index_nas.html | 2 +- machines/templates/machines/index_role.html | 2 +- machines/templates/machines/index_service.html | 2 +- machines/templates/machines/index_sshfp.html | 2 +- machines/templates/machines/index_vlan.html | 2 +- machines/templates/machines/machine.html | 2 +- machines/templates/machines/sidebar.html | 2 +- machines/tests.py | 2 +- machines/urls.py | 2 +- machines/views.py | 2 +- multi_op/apps.py | 2 +- multi_op/forms.py | 2 +- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- multi_op/models.py | 2 +- multi_op/preferences/__init__.py | 2 +- multi_op/preferences/forms.py | 2 +- multi_op/preferences/models.py | 2 +- multi_op/preferences/views.py | 2 +- multi_op/templates/multi_op/aff_room_state.html | 2 +- multi_op/templates/multi_op/index_room_state.html | 2 +- multi_op/templates/multi_op/sidebar.html | 2 +- multi_op/urls.py | 2 +- multi_op/views.py | 2 +- preferences/__init__.py | 2 +- preferences/acl.py | 2 +- preferences/admin.py | 2 +- preferences/api/serializers.py | 2 +- preferences/api/urls.py | 2 +- preferences/api/views.py | 2 +- preferences/forms.py | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/models.py | 2 +- .../templates/preferences/aff_document_template.html | 2 +- preferences/templates/preferences/aff_mailcontact.html | 2 +- preferences/templates/preferences/aff_mandate.html | 2 +- preferences/templates/preferences/aff_radiusattributes.html | 2 +- preferences/templates/preferences/aff_radiuskey.html | 2 +- preferences/templates/preferences/aff_radiusoptions.html | 2 +- preferences/templates/preferences/aff_reminder.html | 2 +- preferences/templates/preferences/aff_service.html | 2 +- .../templates/preferences/aff_switchmanagementcred.html | 2 +- preferences/templates/preferences/delete.html | 2 +- preferences/templates/preferences/display_preferences.html | 2 +- preferences/templates/preferences/edit_preferences.html | 2 +- preferences/templates/preferences/preferences.html | 2 +- preferences/templates/preferences/sidebar.html | 2 +- preferences/templatetags/__init__.py | 2 +- preferences/tests.py | 2 +- preferences/urls.py | 2 +- preferences/views.py | 2 +- re2o/__init__.py | 2 +- re2o/acl.py | 2 +- re2o/aes_field.py | 2 +- re2o/base.py | 2 +- re2o/context_processors.py | 2 +- re2o/field_permissions.py | 2 +- re2o/locale/fr/LC_MESSAGES/django.po | 6 +++--- re2o/login.py | 2 +- re2o/mail_utils.py | 2 +- re2o/management/commands/gen_contrib.py | 2 +- re2o/middleware.py | 2 +- re2o/mixins.py | 2 +- re2o/script_utils.py | 2 +- re2o/settings.py | 2 +- re2o/settings_default.py | 2 +- re2o/settings_local.example.py | 2 +- re2o/templates/re2o/about.html | 4 ++-- re2o/templates/re2o/aff_history.html | 2 +- re2o/templates/re2o/contact.html | 2 +- re2o/templates/re2o/history.html | 2 +- re2o/templates/re2o/index.html | 2 +- re2o/templates/re2o/sidebar.html | 2 +- re2o/templatetags/__init__.py | 2 +- re2o/templatetags/acl.py | 2 +- re2o/templatetags/design.py | 2 +- re2o/templatetags/massive_bootstrap_form.py | 2 +- re2o/templatetags/pagination_extra.py | 2 +- re2o/templatetags/self_adhesion.py | 2 +- re2o/templatetags/url_insert_param.py | 2 +- re2o/urls.py | 2 +- re2o/utils.py | 2 +- re2o/views.py | 2 +- re2o/wsgi.py | 2 +- search/__init__.py | 2 +- search/acl.py | 2 +- search/admin.py | 2 +- search/engine.py | 2 +- search/forms.py | 2 +- search/locale/fr/LC_MESSAGES/django.po | 2 +- search/templates/search/index.html | 2 +- search/templates/search/search.html | 2 +- search/templates/search/sidebar.html | 2 +- search/tests.py | 2 +- search/urls.py | 2 +- search/views.py | 2 +- static/js/collapse-from-url.js | 2 +- static/js/sapphire.js | 2 +- templates/base.html | 2 +- templates/buttons/add.html | 2 +- templates/buttons/edit.html | 2 +- templates/buttons/history.html | 2 +- templates/buttons/multiple_checkbox_alt.html | 2 +- templates/buttons/setlang.html | 2 +- templates/buttons/sort.html | 2 +- templates/buttons/suppr.html | 2 +- templates/buttons/view.html | 2 +- templates/errors/404.html | 2 +- templates/footer.html | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 2 +- templates/nav.html | 2 +- templates/pagination.html | 2 +- templates/registration/login.html | 2 +- templates/sidebar.html | 2 +- test_utils/runner.py | 2 +- tickets/admin.py | 2 +- tickets/forms.py | 2 +- tickets/locale/fr/LC_MESSAGES/django.po | 2 +- tickets/models.py | 2 +- tickets/preferences/__init__.py | 2 +- tickets/preferences/forms.py | 2 +- tickets/preferences/models.py | 2 +- tickets/preferences/views.py | 2 +- tickets/templates/tickets/aff_ticket.html | 2 +- tickets/templates/tickets/aff_tickets.html | 2 +- tickets/templates/tickets/delete.html | 2 +- tickets/templates/tickets/edit.html | 2 +- tickets/templates/tickets/index.html | 2 +- tickets/urls.py | 2 +- tickets/views.py | 2 +- topologie/__init__.py | 2 +- topologie/acl.py | 2 +- topologie/admin.py | 2 +- topologie/api/serializers.py | 2 +- topologie/api/urls.py | 2 +- topologie/api/views.py | 2 +- topologie/forms.py | 2 +- topologie/locale/fr/LC_MESSAGES/django.po | 2 +- topologie/migrations/0001_initial.py | 2 +- topologie/migrations/0002_auto_20160703_1118.py | 2 +- topologie/migrations/0003_room.py | 2 +- topologie/migrations/0004_auto_20160703_1122.py | 2 +- topologie/migrations/0005_auto_20160703_1123.py | 2 +- topologie/migrations/0006_auto_20160703_1129.py | 2 +- topologie/migrations/0007_auto_20160703_1148.py | 2 +- topologie/migrations/0008_port_room.py | 2 +- topologie/migrations/0009_auto_20160703_1200.py | 2 +- topologie/migrations/0010_auto_20160704_2148.py | 2 +- topologie/migrations/0011_auto_20160704_2153.py | 2 +- topologie/migrations/0012_port_machine_interface.py | 2 +- topologie/migrations/0013_port_related.py | 2 +- topologie/migrations/0014_auto_20160706_1238.py | 2 +- topologie/migrations/0015_auto_20160706_1452.py | 2 +- topologie/migrations/0016_auto_20160706_1531.py | 2 +- topologie/migrations/0017_auto_20160718_1141.py | 2 +- topologie/migrations/0018_room_details.py | 2 +- topologie/migrations/0019_auto_20161026_1348.py | 2 +- topologie/migrations/0020_auto_20161119_0033.py | 2 +- topologie/migrations/0021_port_radius.py | 2 +- topologie/migrations/0022_auto_20161211_1622.py | 2 +- topologie/migrations/__init__.py | 2 +- topologie/models.py | 2 +- topologie/templates/topologie/aff_ap.html | 2 +- topologie/templates/topologie/aff_building.html | 2 +- topologie/templates/topologie/aff_chambres.html | 2 +- topologie/templates/topologie/aff_constructor_switch.html | 2 +- topologie/templates/topologie/aff_dormitory.html | 2 +- topologie/templates/topologie/aff_model_switch.html | 2 +- topologie/templates/topologie/aff_modules.html | 2 +- topologie/templates/topologie/aff_port.html | 2 +- topologie/templates/topologie/aff_port_profile.html | 2 +- topologie/templates/topologie/aff_repr_switch.html | 2 +- topologie/templates/topologie/aff_stacks.html | 2 +- topologie/templates/topologie/aff_switch.html | 2 +- topologie/templates/topologie/aff_switch_bay.html | 2 +- topologie/templates/topologie/aff_vlanoptions.html | 2 +- topologie/templates/topologie/delete.html | 2 +- topologie/templates/topologie/edit_stack_sw.html | 2 +- topologie/templates/topologie/index.html | 2 +- topologie/templates/topologie/index_ap.html | 2 +- topologie/templates/topologie/index_building.html | 2 +- topologie/templates/topologie/index_dormitory.html | 2 +- topologie/templates/topologie/index_model_switch.html | 2 +- topologie/templates/topologie/index_module.html | 2 +- topologie/templates/topologie/index_p.html | 2 +- topologie/templates/topologie/index_portprofile.html | 2 +- topologie/templates/topologie/index_room.html | 2 +- topologie/templates/topologie/index_stack.html | 2 +- topologie/templates/topologie/index_switch_bay.html | 2 +- topologie/templates/topologie/sidebar.html | 2 +- topologie/templates/topologie/switch.html | 2 +- topologie/templates/topologie/topo.html | 2 +- topologie/templates/topologie/topo_more.html | 2 +- topologie/tests.py | 2 +- topologie/urls.py | 2 +- topologie/views.py | 2 +- users/__init__.py | 2 +- users/acl.py | 2 +- users/admin.py | 2 +- users/api/serializers.py | 2 +- users/api/urls.py | 2 +- users/api/views.py | 2 +- users/forms.py | 2 +- users/locale/fr/LC_MESSAGES/django.po | 2 +- users/management/commands/archive.py | 2 +- users/management/commands/chgpass.py | 2 +- users/management/commands/chsh.py | 2 +- users/management/commands/derniere_connexion.py | 2 +- users/migrations/0001_initial.py | 2 +- users/migrations/0002_auto_20160630_2301.py | 2 +- users/migrations/0003_listrights_rights.py | 2 +- users/migrations/0004_auto_20160701_2312.py | 2 +- users/migrations/0005_auto_20160702_0006.py | 2 +- users/migrations/0006_ban.py | 2 +- users/migrations/0007_auto_20160702_2322.py | 2 +- users/migrations/0008_user_registered.py | 2 +- users/migrations/0009_user_room.py | 2 +- users/migrations/0010_auto_20160703_1226.py | 2 +- users/migrations/0011_auto_20160703_1227.py | 2 +- users/migrations/0012_auto_20160703_1230.py | 2 +- users/migrations/0013_auto_20160704_1547.py | 2 +- users/migrations/0014_auto_20160704_1548.py | 2 +- users/migrations/0015_whitelist.py | 2 +- users/migrations/0016_auto_20160706_1220.py | 2 +- users/migrations/0017_auto_20160707_0105.py | 2 +- users/migrations/0018_auto_20160707_0115.py | 2 +- users/migrations/0019_auto_20160708_1633.py | 2 +- users/migrations/0020_request.py | 2 +- users/migrations/0021_ldapuser.py | 2 +- users/migrations/0022_ldapuser_sambasid.py | 2 +- users/migrations/0023_auto_20160724_1908.py | 2 +- users/migrations/0024_remove_ldapuser_mac_list.py | 2 +- users/migrations/0025_listshell.py | 2 +- users/migrations/0026_user_shell.py | 2 +- users/migrations/0027_auto_20160726_0216.py | 2 +- users/migrations/0028_auto_20160726_0227.py | 2 +- users/migrations/0029_auto_20160726_0229.py | 2 +- users/migrations/0030_auto_20160726_0357.py | 2 +- users/migrations/0031_auto_20160726_0359.py | 2 +- users/migrations/0032_auto_20160727_2122.py | 2 +- users/migrations/0033_remove_ldapuser_loginshell.py | 2 +- users/migrations/0034_auto_20161018_0037.py | 2 +- users/migrations/0035_auto_20161018_0046.py | 2 +- users/migrations/0036_auto_20161022_2146.py | 2 +- users/migrations/0037_auto_20161028_1906.py | 2 +- users/migrations/0038_auto_20161031_0258.py | 2 +- users/migrations/0039_auto_20161119_0033.py | 2 +- users/migrations/0040_auto_20161119_1709.py | 2 +- users/migrations/0041_listright_details.py | 2 +- users/migrations/0042_auto_20161126_2028.py | 2 +- users/migrations/__init__.py | 2 +- users/models.py | 2 +- users/templates/users/aff_bans.html | 2 +- users/templates/users/aff_clubs.html | 2 +- users/templates/users/aff_emailaddress.html | 2 +- users/templates/users/aff_listright.html | 2 +- users/templates/users/aff_rights.html | 2 +- users/templates/users/aff_schools.html | 2 +- users/templates/users/aff_serviceusers.html | 2 +- users/templates/users/aff_shell.html | 2 +- users/templates/users/aff_users.html | 2 +- users/templates/users/aff_whitelists.html | 2 +- users/templates/users/confirm_email.html | 2 +- users/templates/users/delete.html | 2 +- users/templates/users/edit_listright.html | 2 +- users/templates/users/index.html | 2 +- users/templates/users/index_ban.html | 2 +- users/templates/users/index_clubs.html | 2 +- users/templates/users/index_emailaddress.html | 2 +- users/templates/users/index_listright.html | 2 +- users/templates/users/index_rights.html | 2 +- users/templates/users/index_schools.html | 2 +- users/templates/users/index_serviceusers.html | 2 +- users/templates/users/index_shell.html | 2 +- users/templates/users/index_whitelist.html | 2 +- users/templates/users/mass_archive.html | 2 +- users/templates/users/plugin_out.html | 2 +- users/templates/users/profil.html | 2 +- users/templates/users/resend_confirmation_email.html | 2 +- users/templates/users/sidebar.html | 2 +- users/templates/users/user.html | 2 +- users/templates/users/user_autocapture.html | 2 +- users/tests.py | 2 +- users/urls.py | 2 +- users/views.py | 2 +- 473 files changed, 477 insertions(+), 477 deletions(-) diff --git a/README.md b/README.md index e5ca4cdf..1e76daa9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ GNU public license v2.0 ## Avant propos -Re2o est un logiciel d'administration développé initialement au [rezometz](https://www.rezometz.org/). Il +Re2o est un logiciel d'administration développé initialement au [Rézo Metz](https://www.rezometz.org/). Il se veut agnostique au réseau considéré, de manière à être installable et configurable facilement. Il utilise le framework django avec python3. Il permet de gérer les adhérents, @@ -40,7 +40,7 @@ GNU Public license v2.0 ## Foreword -Re2o is a management software initially developed at [rezometz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup. +Re2o is a management software initially developed at [Rézo Metz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup. Re2o is based on the Django framework and Python3. Its core functionalities include managing the members, their machines, their invoices and their rights to the network but also the topology of the network and its devices. On top of this, it is possible to plug services to enhance the possibilities and fit the need of each organization. diff --git a/api/acl.py b/api/acl.py index 181105c9..829f8ea9 100644 --- a/api/acl.py +++ b/api/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/authentication.py b/api/authentication.py index 88922987..1fa381e0 100644 --- a/api/authentication.py +++ b/api/authentication.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 648f1aaf..7e0244f5 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/pagination.py b/api/pagination.py index 79da050e..8aba9db7 100644 --- a/api/pagination.py +++ b/api/pagination.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/permissions.py b/api/permissions.py index b54adf6f..1983bdc8 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/routers.py b/api/routers.py index 5841d09c..d81ab402 100644 --- a/api/routers.py +++ b/api/routers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/serializers.py b/api/serializers.py index 921a4ac6..de57e26a 100644 --- a/api/serializers.py +++ b/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/settings.py b/api/settings.py index 1435aee6..31cbec26 100644 --- a/api/settings.py +++ b/api/settings.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/tests.py b/api/tests.py index df0b7a7d..32c1e264 100644 --- a/api/tests.py +++ b/api/tests.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/urls.py b/api/urls.py index 624b838b..e64c1eec 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/api/views.py b/api/views.py index 4ab37609..70ff33bf 100644 --- a/api/views.py +++ b/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/__init__.py b/cotisations/__init__.py index be5cb5d2..55a37d60 100644 --- a/cotisations/__init__.py +++ b/cotisations/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/acl.py b/cotisations/acl.py index 01c685e3..08429a3d 100644 --- a/cotisations/acl.py +++ b/cotisations/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/admin.py b/cotisations/admin.py index 0519e819..ea280fca 100644 --- a/cotisations/admin.py +++ b/cotisations/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/api/serializers.py b/cotisations/api/serializers.py index c3b25ef7..f7279ea9 100644 --- a/cotisations/api/serializers.py +++ b/cotisations/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/api/urls.py b/cotisations/api/urls.py index 33834b26..85e60c72 100644 --- a/cotisations/api/urls.py +++ b/cotisations/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/api/views.py b/cotisations/api/views.py index 246c64a9..2995e721 100644 --- a/cotisations/api/views.py +++ b/cotisations/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/forms.py b/cotisations/forms.py index 76db358c..0f135963 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 400171a2..52b0d104 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0001_initial.py b/cotisations/migrations/0001_initial.py index 0ffe83c9..d2249c76 100644 --- a/cotisations/migrations/0001_initial.py +++ b/cotisations/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0002_remove_facture_article.py b/cotisations/migrations/0002_remove_facture_article.py index 5a4e68f1..2f9f646c 100644 --- a/cotisations/migrations/0002_remove_facture_article.py +++ b/cotisations/migrations/0002_remove_facture_article.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0003_auto_20160702_1448.py b/cotisations/migrations/0003_auto_20160702_1448.py index 29c4f180..63a84100 100644 --- a/cotisations/migrations/0003_auto_20160702_1448.py +++ b/cotisations/migrations/0003_auto_20160702_1448.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0004_auto_20160702_1528.py b/cotisations/migrations/0004_auto_20160702_1528.py index d0181a64..df65ae98 100644 --- a/cotisations/migrations/0004_auto_20160702_1528.py +++ b/cotisations/migrations/0004_auto_20160702_1528.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0005_auto_20160702_1532.py b/cotisations/migrations/0005_auto_20160702_1532.py index 6017ec59..1cc613fe 100644 --- a/cotisations/migrations/0005_auto_20160702_1532.py +++ b/cotisations/migrations/0005_auto_20160702_1532.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0006_auto_20160702_1534.py b/cotisations/migrations/0006_auto_20160702_1534.py index 750fd5b2..0fe78816 100644 --- a/cotisations/migrations/0006_auto_20160702_1534.py +++ b/cotisations/migrations/0006_auto_20160702_1534.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0007_auto_20160702_1543.py b/cotisations/migrations/0007_auto_20160702_1543.py index 0755befb..5becf23a 100644 --- a/cotisations/migrations/0007_auto_20160702_1543.py +++ b/cotisations/migrations/0007_auto_20160702_1543.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0008_auto_20160702_1614.py b/cotisations/migrations/0008_auto_20160702_1614.py index c3490ceb..774a93c1 100644 --- a/cotisations/migrations/0008_auto_20160702_1614.py +++ b/cotisations/migrations/0008_auto_20160702_1614.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0009_remove_cotisation_user.py b/cotisations/migrations/0009_remove_cotisation_user.py index 784e6205..3c20c442 100644 --- a/cotisations/migrations/0009_remove_cotisation_user.py +++ b/cotisations/migrations/0009_remove_cotisation_user.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0010_auto_20160702_1840.py b/cotisations/migrations/0010_auto_20160702_1840.py index cdae642e..1c6aeee1 100644 --- a/cotisations/migrations/0010_auto_20160702_1840.py +++ b/cotisations/migrations/0010_auto_20160702_1840.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0011_auto_20160702_1911.py b/cotisations/migrations/0011_auto_20160702_1911.py index fec46c36..8033bff7 100644 --- a/cotisations/migrations/0011_auto_20160702_1911.py +++ b/cotisations/migrations/0011_auto_20160702_1911.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0012_auto_20160704_0118.py b/cotisations/migrations/0012_auto_20160704_0118.py index 0e2ac7ae..875a5e48 100644 --- a/cotisations/migrations/0012_auto_20160704_0118.py +++ b/cotisations/migrations/0012_auto_20160704_0118.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0013_auto_20160711_2240.py b/cotisations/migrations/0013_auto_20160711_2240.py index cfff6a7a..38899650 100644 --- a/cotisations/migrations/0013_auto_20160711_2240.py +++ b/cotisations/migrations/0013_auto_20160711_2240.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0014_auto_20160712_0245.py b/cotisations/migrations/0014_auto_20160712_0245.py index 1a7bd48a..0121e241 100644 --- a/cotisations/migrations/0014_auto_20160712_0245.py +++ b/cotisations/migrations/0014_auto_20160712_0245.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0015_auto_20160714_2142.py b/cotisations/migrations/0015_auto_20160714_2142.py index 636f138f..efb1a733 100644 --- a/cotisations/migrations/0015_auto_20160714_2142.py +++ b/cotisations/migrations/0015_auto_20160714_2142.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/0016_auto_20160715_0110.py b/cotisations/migrations/0016_auto_20160715_0110.py index f7e00396..e716960a 100644 --- a/cotisations/migrations/0016_auto_20160715_0110.py +++ b/cotisations/migrations/0016_auto_20160715_0110.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/migrations/__init__.py b/cotisations/migrations/__init__.py index b409e525..fecd9684 100644 --- a/cotisations/migrations/__init__.py +++ b/cotisations/migrations/__init__.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/models.py b/cotisations/models.py index 4963e0ad..b3480561 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/__init__.py b/cotisations/payment_methods/__init__.py index cbb9c4a6..1170bd92 100644 --- a/cotisations/payment_methods/__init__.py +++ b/cotisations/payment_methods/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/balance/__init__.py b/cotisations/payment_methods/balance/__init__.py index ebfaaddd..386a582d 100644 --- a/cotisations/payment_methods/balance/__init__.py +++ b/cotisations/payment_methods/balance/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/balance/models.py b/cotisations/payment_methods/balance/models.py index afa43c48..9f07f930 100644 --- a/cotisations/payment_methods/balance/models.py +++ b/cotisations/payment_methods/balance/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/cheque/__init__.py b/cotisations/payment_methods/cheque/__init__.py index 27e985e5..012b06fd 100644 --- a/cotisations/payment_methods/cheque/__init__.py +++ b/cotisations/payment_methods/cheque/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/cheque/forms.py b/cotisations/payment_methods/cheque/forms.py index 370a701d..f83cc8b3 100644 --- a/cotisations/payment_methods/cheque/forms.py +++ b/cotisations/payment_methods/cheque/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/cheque/models.py b/cotisations/payment_methods/cheque/models.py index 62479f22..c05099ab 100644 --- a/cotisations/payment_methods/cheque/models.py +++ b/cotisations/payment_methods/cheque/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/cheque/urls.py b/cotisations/payment_methods/cheque/urls.py index a29e1b8c..0187ae53 100644 --- a/cotisations/payment_methods/cheque/urls.py +++ b/cotisations/payment_methods/cheque/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/cheque/views.py b/cotisations/payment_methods/cheque/views.py index 89861b9a..191e4159 100644 --- a/cotisations/payment_methods/cheque/views.py +++ b/cotisations/payment_methods/cheque/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/comnpay/__init__.py b/cotisations/payment_methods/comnpay/__init__.py index 0cfcfab5..b84485c8 100644 --- a/cotisations/payment_methods/comnpay/__init__.py +++ b/cotisations/payment_methods/comnpay/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/comnpay/models.py b/cotisations/payment_methods/comnpay/models.py index 2c46f685..ef0c6cf5 100644 --- a/cotisations/payment_methods/comnpay/models.py +++ b/cotisations/payment_methods/comnpay/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/comnpay/urls.py b/cotisations/payment_methods/comnpay/urls.py index 69bb3b38..babf2448 100644 --- a/cotisations/payment_methods/comnpay/urls.py +++ b/cotisations/payment_methods/comnpay/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/comnpay/views.py b/cotisations/payment_methods/comnpay/views.py index cddb185a..38bcbf96 100644 --- a/cotisations/payment_methods/comnpay/views.py +++ b/cotisations/payment_methods/comnpay/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/forms.py b/cotisations/payment_methods/forms.py index a6bac3ed..447fa38f 100644 --- a/cotisations/payment_methods/forms.py +++ b/cotisations/payment_methods/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/free/__init__.py b/cotisations/payment_methods/free/__init__.py index 27041f2e..e79b8085 100644 --- a/cotisations/payment_methods/free/__init__.py +++ b/cotisations/payment_methods/free/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/free/models.py b/cotisations/payment_methods/free/models.py index 39a3aa80..a4c24459 100644 --- a/cotisations/payment_methods/free/models.py +++ b/cotisations/payment_methods/free/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/mixins.py b/cotisations/payment_methods/mixins.py index 1e808f09..8c3b1dc9 100644 --- a/cotisations/payment_methods/mixins.py +++ b/cotisations/payment_methods/mixins.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/note_kfet/__init__.py b/cotisations/payment_methods/note_kfet/__init__.py index 99949bbc..90c99795 100644 --- a/cotisations/payment_methods/note_kfet/__init__.py +++ b/cotisations/payment_methods/note_kfet/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/note_kfet/forms.py b/cotisations/payment_methods/note_kfet/forms.py index 7d82b93f..f2c292e3 100644 --- a/cotisations/payment_methods/note_kfet/forms.py +++ b/cotisations/payment_methods/note_kfet/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/note_kfet/models.py b/cotisations/payment_methods/note_kfet/models.py index e83cfb36..4f1a8152 100644 --- a/cotisations/payment_methods/note_kfet/models.py +++ b/cotisations/payment_methods/note_kfet/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/note_kfet/urls.py b/cotisations/payment_methods/note_kfet/urls.py index 89bb3eb9..a7fe3046 100644 --- a/cotisations/payment_methods/note_kfet/urls.py +++ b/cotisations/payment_methods/note_kfet/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/note_kfet/views.py b/cotisations/payment_methods/note_kfet/views.py index 4069a8f5..a3fb54b3 100644 --- a/cotisations/payment_methods/note_kfet/views.py +++ b/cotisations/payment_methods/note_kfet/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/payment_methods/urls.py b/cotisations/payment_methods/urls.py index adb606bc..9a06f497 100644 --- a/cotisations/payment_methods/urls.py +++ b/cotisations/payment_methods/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/templates/cotisations/aff_article.html b/cotisations/templates/cotisations/aff_article.html index f53a71d2..d157f00f 100644 --- a/cotisations/templates/cotisations/aff_article.html +++ b/cotisations/templates/cotisations/aff_article.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_banque.html b/cotisations/templates/cotisations/aff_banque.html index 1bf1fcd2..0fb64c2c 100644 --- a/cotisations/templates/cotisations/aff_banque.html +++ b/cotisations/templates/cotisations/aff_banque.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_cost_estimate.html b/cotisations/templates/cotisations/aff_cost_estimate.html index eb040dce..fe654744 100644 --- a/cotisations/templates/cotisations/aff_cost_estimate.html +++ b/cotisations/templates/cotisations/aff_cost_estimate.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index 4d2cd3ee..e48a6e2b 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_custom_invoice.html b/cotisations/templates/cotisations/aff_custom_invoice.html index c1c5a396..c1c24760 100644 --- a/cotisations/templates/cotisations/aff_custom_invoice.html +++ b/cotisations/templates/cotisations/aff_custom_invoice.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_paiement.html b/cotisations/templates/cotisations/aff_paiement.html index 6043da67..d41973a6 100644 --- a/cotisations/templates/cotisations/aff_paiement.html +++ b/cotisations/templates/cotisations/aff_paiement.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/aff_profil.html b/cotisations/templates/cotisations/aff_profil.html index 28da5341..e52b334e 100644 --- a/cotisations/templates/cotisations/aff_profil.html +++ b/cotisations/templates/cotisations/aff_profil.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/control.html b/cotisations/templates/cotisations/control.html index 497de6f4..04ff6ca1 100644 --- a/cotisations/templates/cotisations/control.html +++ b/cotisations/templates/cotisations/control.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/delete.html b/cotisations/templates/cotisations/delete.html index e6f1b362..836b5b3e 100644 --- a/cotisations/templates/cotisations/delete.html +++ b/cotisations/templates/cotisations/delete.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/edit_facture.html b/cotisations/templates/cotisations/edit_facture.html index 99dd2cd8..ca55cb66 100644 --- a/cotisations/templates/cotisations/edit_facture.html +++ b/cotisations/templates/cotisations/edit_facture.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/facture.html b/cotisations/templates/cotisations/facture.html index dc9b31f7..89f65bca 100644 --- a/cotisations/templates/cotisations/facture.html +++ b/cotisations/templates/cotisations/facture.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index.html b/cotisations/templates/cotisations/index.html index ba3a3ea4..e60530c6 100644 --- a/cotisations/templates/cotisations/index.html +++ b/cotisations/templates/cotisations/index.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index_article.html b/cotisations/templates/cotisations/index_article.html index 1a4c3c8d..f6117da2 100644 --- a/cotisations/templates/cotisations/index_article.html +++ b/cotisations/templates/cotisations/index_article.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index_banque.html b/cotisations/templates/cotisations/index_banque.html index c653acfd..23218188 100644 --- a/cotisations/templates/cotisations/index_banque.html +++ b/cotisations/templates/cotisations/index_banque.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index_cost_estimate.html b/cotisations/templates/cotisations/index_cost_estimate.html index 0d2fad01..8939aa3c 100644 --- a/cotisations/templates/cotisations/index_cost_estimate.html +++ b/cotisations/templates/cotisations/index_cost_estimate.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index_custom_invoice.html b/cotisations/templates/cotisations/index_custom_invoice.html index cbc502d2..99c48df4 100644 --- a/cotisations/templates/cotisations/index_custom_invoice.html +++ b/cotisations/templates/cotisations/index_custom_invoice.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/index_paiement.html b/cotisations/templates/cotisations/index_paiement.html index 1411add0..e56f20b7 100644 --- a/cotisations/templates/cotisations/index_paiement.html +++ b/cotisations/templates/cotisations/index_paiement.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/payment.html b/cotisations/templates/cotisations/payment.html index 39dbb195..a6f9842e 100644 --- a/cotisations/templates/cotisations/payment.html +++ b/cotisations/templates/cotisations/payment.html @@ -1,6 +1,6 @@ {% extends 'cotisations/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/templates/cotisations/sidebar.html b/cotisations/templates/cotisations/sidebar.html index 4b0f3392..47f6f460 100644 --- a/cotisations/templates/cotisations/sidebar.html +++ b/cotisations/templates/cotisations/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/cotisations/tex.py b/cotisations/tex.py index 2930fffe..fd6b6cc4 100644 --- a/cotisations/tex.py +++ b/cotisations/tex.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/urls.py b/cotisations/urls.py index e0a3aa16..6baf74c7 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/utils.py b/cotisations/utils.py index 47a83885..8166b0be 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/cotisations/views.py b/cotisations/views.py index 800c9df8..35fdc82a 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/freeradius_utils/auth.py b/freeradius_utils/auth.py index b2ec571a..f4201f44 100644 --- a/freeradius_utils/auth.py +++ b/freeradius_utils/auth.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/__init__.py b/logs/__init__.py index 32f6fa7d..8bdd5e43 100644 --- a/logs/__init__.py +++ b/logs/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/acl.py b/logs/acl.py index 3c94426e..d439b4a6 100644 --- a/logs/acl.py +++ b/logs/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/forms.py b/logs/forms.py index 5cdeed7c..b8c8b010 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 30536e7a..2bf4b6e7 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/models.py b/logs/models.py index 993664a1..8790f46e 100644 --- a/logs/models.py +++ b/logs/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/templates/logs/aff_stats_general.html b/logs/templates/logs/aff_stats_general.html index 48d79b92..bbf5d680 100644 --- a/logs/templates/logs/aff_stats_general.html +++ b/logs/templates/logs/aff_stats_general.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/aff_stats_logs.html b/logs/templates/logs/aff_stats_logs.html index 3ba37958..1efa01e0 100644 --- a/logs/templates/logs/aff_stats_logs.html +++ b/logs/templates/logs/aff_stats_logs.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/aff_stats_models.html b/logs/templates/logs/aff_stats_models.html index 93e14109..bc216a13 100644 --- a/logs/templates/logs/aff_stats_models.html +++ b/logs/templates/logs/aff_stats_models.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/aff_stats_users.html b/logs/templates/logs/aff_stats_users.html index 4978b2ad..63ed1f2c 100644 --- a/logs/templates/logs/aff_stats_users.html +++ b/logs/templates/logs/aff_stats_users.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/aff_summary.html b/logs/templates/logs/aff_summary.html index 31834a2d..a5cc24ba 100644 --- a/logs/templates/logs/aff_summary.html +++ b/logs/templates/logs/aff_summary.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/delete.html b/logs/templates/logs/delete.html index 3bd0d638..1df4599b 100644 --- a/logs/templates/logs/delete.html +++ b/logs/templates/logs/delete.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/index.html b/logs/templates/logs/index.html index cc446f5b..7ffbad47 100644 --- a/logs/templates/logs/index.html +++ b/logs/templates/logs/index.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/machine_history.html b/logs/templates/logs/machine_history.html index 5aea5e9d..ff7b1f74 100644 --- a/logs/templates/logs/machine_history.html +++ b/logs/templates/logs/machine_history.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/search_machine_history.html b/logs/templates/logs/search_machine_history.html index 07c9f68c..0e03a8bf 100644 --- a/logs/templates/logs/search_machine_history.html +++ b/logs/templates/logs/search_machine_history.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/search_stats_logs.html b/logs/templates/logs/search_stats_logs.html index 5211d336..b124139e 100644 --- a/logs/templates/logs/search_stats_logs.html +++ b/logs/templates/logs/search_stats_logs.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/sidebar.html b/logs/templates/logs/sidebar.html index 4b0f3392..47f6f460 100644 --- a/logs/templates/logs/sidebar.html +++ b/logs/templates/logs/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/stats_general.html b/logs/templates/logs/stats_general.html index e7021716..3c482943 100644 --- a/logs/templates/logs/stats_general.html +++ b/logs/templates/logs/stats_general.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/stats_logs.html b/logs/templates/logs/stats_logs.html index e3314cb2..1746d994 100644 --- a/logs/templates/logs/stats_logs.html +++ b/logs/templates/logs/stats_logs.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/stats_models.html b/logs/templates/logs/stats_models.html index 03b82b37..dcf74400 100644 --- a/logs/templates/logs/stats_models.html +++ b/logs/templates/logs/stats_models.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templates/logs/stats_users.html b/logs/templates/logs/stats_users.html index de706833..441d334e 100644 --- a/logs/templates/logs/stats_users.html +++ b/logs/templates/logs/stats_users.html @@ -1,6 +1,6 @@ {% extends 'logs/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/logs/templatetags/__init__.py b/logs/templatetags/__init__.py index b8b9a128..44fbaa54 100644 --- a/logs/templatetags/__init__.py +++ b/logs/templatetags/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/templatetags/logs_extra.py b/logs/templatetags/logs_extra.py index c436c1fa..73c97b54 100644 --- a/logs/templatetags/logs_extra.py +++ b/logs/templatetags/logs_extra.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/tests.py b/logs/tests.py index 51ef33ae..6d966747 100644 --- a/logs/tests.py +++ b/logs/tests.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/urls.py b/logs/urls.py index 327ccb8a..deb7ab30 100644 --- a/logs/urls.py +++ b/logs/urls.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/logs/views.py b/logs/views.py index e67aefc3..7b01c4a5 100644 --- a/logs/views.py +++ b/logs/views.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/__init__.py b/machines/__init__.py index 4f7225d5..71d5dbc1 100644 --- a/machines/__init__.py +++ b/machines/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/acl.py b/machines/acl.py index e8b97c62..d28416fc 100644 --- a/machines/acl.py +++ b/machines/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/admin.py b/machines/admin.py index ee6ea6f1..dc2817f0 100644 --- a/machines/admin.py +++ b/machines/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/api/serializers.py b/machines/api/serializers.py index 442c7791..0c8b7ffe 100644 --- a/machines/api/serializers.py +++ b/machines/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/api/urls.py b/machines/api/urls.py index 3cf82122..3542e28c 100644 --- a/machines/api/urls.py +++ b/machines/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/api/views.py b/machines/api/views.py index 3e510d8e..4fa23d8e 100644 --- a/machines/api/views.py +++ b/machines/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/forms.py b/machines/forms.py index 30fa0e69..03f1ecc7 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index de538913..1709f5e6 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0001_initial.py b/machines/migrations/0001_initial.py index e253acdd..6986a798 100644 --- a/machines/migrations/0001_initial.py +++ b/machines/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0002_auto_20160703_1444.py b/machines/migrations/0002_auto_20160703_1444.py index 83d820a4..6dd2e4c2 100644 --- a/machines/migrations/0002_auto_20160703_1444.py +++ b/machines/migrations/0002_auto_20160703_1444.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0003_auto_20160703_1450.py b/machines/migrations/0003_auto_20160703_1450.py index 33c3642e..71cc95d0 100644 --- a/machines/migrations/0003_auto_20160703_1450.py +++ b/machines/migrations/0003_auto_20160703_1450.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0004_auto_20160703_1451.py b/machines/migrations/0004_auto_20160703_1451.py index df36fb0f..c171851c 100644 --- a/machines/migrations/0004_auto_20160703_1451.py +++ b/machines/migrations/0004_auto_20160703_1451.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0005_auto_20160703_1523.py b/machines/migrations/0005_auto_20160703_1523.py index 481a113d..5dcec84c 100644 --- a/machines/migrations/0005_auto_20160703_1523.py +++ b/machines/migrations/0005_auto_20160703_1523.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0006_auto_20160703_1813.py b/machines/migrations/0006_auto_20160703_1813.py index 19ce0a75..7269bb5d 100644 --- a/machines/migrations/0006_auto_20160703_1813.py +++ b/machines/migrations/0006_auto_20160703_1813.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0007_auto_20160703_1816.py b/machines/migrations/0007_auto_20160703_1816.py index be74d8b7..797f7984 100644 --- a/machines/migrations/0007_auto_20160703_1816.py +++ b/machines/migrations/0007_auto_20160703_1816.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0008_remove_interface_ipv6.py b/machines/migrations/0008_remove_interface_ipv6.py index f4ebdfbb..6683b265 100644 --- a/machines/migrations/0008_remove_interface_ipv6.py +++ b/machines/migrations/0008_remove_interface_ipv6.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0009_auto_20160703_2358.py b/machines/migrations/0009_auto_20160703_2358.py index 682f490e..d9e17d8b 100644 --- a/machines/migrations/0009_auto_20160703_2358.py +++ b/machines/migrations/0009_auto_20160703_2358.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0010_auto_20160704_0104.py b/machines/migrations/0010_auto_20160704_0104.py index 79ffc90e..4485c065 100644 --- a/machines/migrations/0010_auto_20160704_0104.py +++ b/machines/migrations/0010_auto_20160704_0104.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0011_auto_20160704_0105.py b/machines/migrations/0011_auto_20160704_0105.py index b780b951..4ec76da8 100644 --- a/machines/migrations/0011_auto_20160704_0105.py +++ b/machines/migrations/0011_auto_20160704_0105.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0012_auto_20160704_0118.py b/machines/migrations/0012_auto_20160704_0118.py index 222b55a5..1b206380 100644 --- a/machines/migrations/0012_auto_20160704_0118.py +++ b/machines/migrations/0012_auto_20160704_0118.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0013_auto_20160705_1014.py b/machines/migrations/0013_auto_20160705_1014.py index 5ee18020..39c430b3 100644 --- a/machines/migrations/0013_auto_20160705_1014.py +++ b/machines/migrations/0013_auto_20160705_1014.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0014_auto_20160706_1220.py b/machines/migrations/0014_auto_20160706_1220.py index 2d3fc351..cce8f9ae 100644 --- a/machines/migrations/0014_auto_20160706_1220.py +++ b/machines/migrations/0014_auto_20160706_1220.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0015_auto_20160707_0105.py b/machines/migrations/0015_auto_20160707_0105.py index 19022f55..1a0d0f6a 100644 --- a/machines/migrations/0015_auto_20160707_0105.py +++ b/machines/migrations/0015_auto_20160707_0105.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0016_auto_20160708_1633.py b/machines/migrations/0016_auto_20160708_1633.py index 1b4a6bd0..c95bc7d7 100644 --- a/machines/migrations/0016_auto_20160708_1633.py +++ b/machines/migrations/0016_auto_20160708_1633.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0017_auto_20160708_1645.py b/machines/migrations/0017_auto_20160708_1645.py index 455070e4..2e31332a 100644 --- a/machines/migrations/0017_auto_20160708_1645.py +++ b/machines/migrations/0017_auto_20160708_1645.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0018_auto_20160708_1813.py b/machines/migrations/0018_auto_20160708_1813.py index bf2806cc..e0964e09 100644 --- a/machines/migrations/0018_auto_20160708_1813.py +++ b/machines/migrations/0018_auto_20160708_1813.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0019_auto_20160718_1141.py b/machines/migrations/0019_auto_20160718_1141.py index 6990d85f..23f26a00 100644 --- a/machines/migrations/0019_auto_20160718_1141.py +++ b/machines/migrations/0019_auto_20160718_1141.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0020_auto_20160718_1849.py b/machines/migrations/0020_auto_20160718_1849.py index 925e9cd5..c8d372ac 100644 --- a/machines/migrations/0020_auto_20160718_1849.py +++ b/machines/migrations/0020_auto_20160718_1849.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0021_auto_20161006_1943.py b/machines/migrations/0021_auto_20161006_1943.py index 688fe503..07be00b5 100644 --- a/machines/migrations/0021_auto_20161006_1943.py +++ b/machines/migrations/0021_auto_20161006_1943.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0022_auto_20161011_1829.py b/machines/migrations/0022_auto_20161011_1829.py index bae2d679..0ceeeeaf 100644 --- a/machines/migrations/0022_auto_20161011_1829.py +++ b/machines/migrations/0022_auto_20161011_1829.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0023_iplist_ip_type.py b/machines/migrations/0023_iplist_ip_type.py index 23f1a0f7..28320bb5 100644 --- a/machines/migrations/0023_iplist_ip_type.py +++ b/machines/migrations/0023_iplist_ip_type.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0024_machinetype_need_infra.py b/machines/migrations/0024_machinetype_need_infra.py index 4caf9fa0..0ab4c3f2 100644 --- a/machines/migrations/0024_machinetype_need_infra.py +++ b/machines/migrations/0024_machinetype_need_infra.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0025_auto_20161023_0038.py b/machines/migrations/0025_auto_20161023_0038.py index 285e9271..3e82e55b 100644 --- a/machines/migrations/0025_auto_20161023_0038.py +++ b/machines/migrations/0025_auto_20161023_0038.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0026_auto_20161026_1348.py b/machines/migrations/0026_auto_20161026_1348.py index 22911933..59f00fbd 100644 --- a/machines/migrations/0026_auto_20161026_1348.py +++ b/machines/migrations/0026_auto_20161026_1348.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0027_alias.py b/machines/migrations/0027_alias.py index c4d85cd7..ae63c3f7 100644 --- a/machines/migrations/0027_alias.py +++ b/machines/migrations/0027_alias.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0028_iptype_domaine_ip.py b/machines/migrations/0028_iptype_domaine_ip.py index d0bfa5b4..1928413f 100644 --- a/machines/migrations/0028_iptype_domaine_ip.py +++ b/machines/migrations/0028_iptype_domaine_ip.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0029_iptype_domaine_range.py b/machines/migrations/0029_iptype_domaine_range.py index 30159868..71333465 100644 --- a/machines/migrations/0029_iptype_domaine_range.py +++ b/machines/migrations/0029_iptype_domaine_range.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0030_auto_20161118_1730.py b/machines/migrations/0030_auto_20161118_1730.py index e958fe3e..255f1082 100644 --- a/machines/migrations/0030_auto_20161118_1730.py +++ b/machines/migrations/0030_auto_20161118_1730.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0031_auto_20161119_1709.py b/machines/migrations/0031_auto_20161119_1709.py index ae2ff82f..62955a38 100644 --- a/machines/migrations/0031_auto_20161119_1709.py +++ b/machines/migrations/0031_auto_20161119_1709.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0032_auto_20161119_1850.py b/machines/migrations/0032_auto_20161119_1850.py index 7a788b42..d0c0bf54 100644 --- a/machines/migrations/0032_auto_20161119_1850.py +++ b/machines/migrations/0032_auto_20161119_1850.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0033_extension_need_infra.py b/machines/migrations/0033_extension_need_infra.py index 1f42f153..0e1692be 100644 --- a/machines/migrations/0033_extension_need_infra.py +++ b/machines/migrations/0033_extension_need_infra.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0034_iplist_need_infra.py b/machines/migrations/0034_iplist_need_infra.py index 0e0dad85..fe01748d 100644 --- a/machines/migrations/0034_iplist_need_infra.py +++ b/machines/migrations/0034_iplist_need_infra.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0035_auto_20161224_1201.py b/machines/migrations/0035_auto_20161224_1201.py index 2f46f048..f181ab7d 100644 --- a/machines/migrations/0035_auto_20161224_1201.py +++ b/machines/migrations/0035_auto_20161224_1201.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0036_auto_20161224_1204.py b/machines/migrations/0036_auto_20161224_1204.py index 80ee84be..abc7a67e 100644 --- a/machines/migrations/0036_auto_20161224_1204.py +++ b/machines/migrations/0036_auto_20161224_1204.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0037_domain_cname.py b/machines/migrations/0037_domain_cname.py index 08570133..be56b5c7 100644 --- a/machines/migrations/0037_domain_cname.py +++ b/machines/migrations/0037_domain_cname.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0038_auto_20161224_1721.py b/machines/migrations/0038_auto_20161224_1721.py index 0056c308..c8515d79 100644 --- a/machines/migrations/0038_auto_20161224_1721.py +++ b/machines/migrations/0038_auto_20161224_1721.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0039_auto_20161224_1732.py b/machines/migrations/0039_auto_20161224_1732.py index 8136fc9c..1ee4fb89 100644 --- a/machines/migrations/0039_auto_20161224_1732.py +++ b/machines/migrations/0039_auto_20161224_1732.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0040_remove_interface_dns.py b/machines/migrations/0040_remove_interface_dns.py index 232e65e7..9b6b35f6 100644 --- a/machines/migrations/0040_remove_interface_dns.py +++ b/machines/migrations/0040_remove_interface_dns.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0041_remove_ns_interface.py b/machines/migrations/0041_remove_ns_interface.py index 8906abd5..d16e9057 100644 --- a/machines/migrations/0041_remove_ns_interface.py +++ b/machines/migrations/0041_remove_ns_interface.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/0042_ns_ns.py b/machines/migrations/0042_ns_ns.py index 1c0d27d1..f9f27c90 100644 --- a/machines/migrations/0042_ns_ns.py +++ b/machines/migrations/0042_ns_ns.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/migrations/__init__.py b/machines/migrations/__init__.py index b409e525..fecd9684 100644 --- a/machines/migrations/__init__.py +++ b/machines/migrations/__init__.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/models.py b/machines/models.py index 9de7d307..309e0dd4 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/templates/machines/aff_alias.html b/machines/templates/machines/aff_alias.html index 8fe7260c..95a15432 100644 --- a/machines/templates/machines/aff_alias.html +++ b/machines/templates/machines/aff_alias.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_dname.html b/machines/templates/machines/aff_dname.html index 4073a388..69d4082f 100644 --- a/machines/templates/machines/aff_dname.html +++ b/machines/templates/machines/aff_dname.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_extension.html b/machines/templates/machines/aff_extension.html index 358fc33e..c152f285 100644 --- a/machines/templates/machines/aff_extension.html +++ b/machines/templates/machines/aff_extension.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_iptype.html b/machines/templates/machines/aff_iptype.html index ce436c6b..35b7cdfa 100644 --- a/machines/templates/machines/aff_iptype.html +++ b/machines/templates/machines/aff_iptype.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_ipv6.html b/machines/templates/machines/aff_ipv6.html index 68cc1dde..144dab14 100644 --- a/machines/templates/machines/aff_ipv6.html +++ b/machines/templates/machines/aff_ipv6.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_machines.html b/machines/templates/machines/aff_machines.html index 75eb47c6..fe2ab648 100644 --- a/machines/templates/machines/aff_machines.html +++ b/machines/templates/machines/aff_machines.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_machinetype.html b/machines/templates/machines/aff_machinetype.html index 087b27d2..a3fdece5 100644 --- a/machines/templates/machines/aff_machinetype.html +++ b/machines/templates/machines/aff_machinetype.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_mx.html b/machines/templates/machines/aff_mx.html index f6fe5fd8..a477d93d 100644 --- a/machines/templates/machines/aff_mx.html +++ b/machines/templates/machines/aff_mx.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_nas.html b/machines/templates/machines/aff_nas.html index 339ff608..49d5c7f9 100644 --- a/machines/templates/machines/aff_nas.html +++ b/machines/templates/machines/aff_nas.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_ns.html b/machines/templates/machines/aff_ns.html index 96534f8a..7b85fc86 100644 --- a/machines/templates/machines/aff_ns.html +++ b/machines/templates/machines/aff_ns.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_portlist.html b/machines/templates/machines/aff_portlist.html index b01f407c..bb754cdf 100644 --- a/machines/templates/machines/aff_portlist.html +++ b/machines/templates/machines/aff_portlist.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_profil.html b/machines/templates/machines/aff_profil.html index e1afb7cc..27095dc5 100644 --- a/machines/templates/machines/aff_profil.html +++ b/machines/templates/machines/aff_profil.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_role.html b/machines/templates/machines/aff_role.html index 11b923d6..eb03be71 100644 --- a/machines/templates/machines/aff_role.html +++ b/machines/templates/machines/aff_role.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_servers.html b/machines/templates/machines/aff_servers.html index 3829c6c1..c2c07910 100644 --- a/machines/templates/machines/aff_servers.html +++ b/machines/templates/machines/aff_servers.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_service.html b/machines/templates/machines/aff_service.html index 535ddf63..fd2600c1 100644 --- a/machines/templates/machines/aff_service.html +++ b/machines/templates/machines/aff_service.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_soa.html b/machines/templates/machines/aff_soa.html index 31905b31..36baba32 100644 --- a/machines/templates/machines/aff_soa.html +++ b/machines/templates/machines/aff_soa.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_srv.html b/machines/templates/machines/aff_srv.html index 42f66fe5..ca624087 100644 --- a/machines/templates/machines/aff_srv.html +++ b/machines/templates/machines/aff_srv.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_sshfp.html b/machines/templates/machines/aff_sshfp.html index ca88d0f4..3ba5b62a 100644 --- a/machines/templates/machines/aff_sshfp.html +++ b/machines/templates/machines/aff_sshfp.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_txt.html b/machines/templates/machines/aff_txt.html index bb140ce8..094bcf2f 100644 --- a/machines/templates/machines/aff_txt.html +++ b/machines/templates/machines/aff_txt.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/aff_vlan.html b/machines/templates/machines/aff_vlan.html index f60424a2..7a408c00 100644 --- a/machines/templates/machines/aff_vlan.html +++ b/machines/templates/machines/aff_vlan.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/delete.html b/machines/templates/machines/delete.html index 3c890c67..34a732d4 100644 --- a/machines/templates/machines/delete.html +++ b/machines/templates/machines/delete.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/edit_portlist.html b/machines/templates/machines/edit_portlist.html index a2aded23..218cbe69 100644 --- a/machines/templates/machines/edit_portlist.html +++ b/machines/templates/machines/edit_portlist.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index.html b/machines/templates/machines/index.html index d83d99b4..36a2e63f 100644 --- a/machines/templates/machines/index.html +++ b/machines/templates/machines/index.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_alias.html b/machines/templates/machines/index_alias.html index 4bf88edc..4d36d94c 100644 --- a/machines/templates/machines/index_alias.html +++ b/machines/templates/machines/index_alias.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_extension.html b/machines/templates/machines/index_extension.html index 26e9efea..9d57d767 100644 --- a/machines/templates/machines/index_extension.html +++ b/machines/templates/machines/index_extension.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_iptype.html b/machines/templates/machines/index_iptype.html index 38a3a8ad..533ed996 100644 --- a/machines/templates/machines/index_iptype.html +++ b/machines/templates/machines/index_iptype.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_ipv6.html b/machines/templates/machines/index_ipv6.html index c9cbe8fd..e39d137f 100644 --- a/machines/templates/machines/index_ipv6.html +++ b/machines/templates/machines/index_ipv6.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_machinetype.html b/machines/templates/machines/index_machinetype.html index 24ac490e..362755f9 100644 --- a/machines/templates/machines/index_machinetype.html +++ b/machines/templates/machines/index_machinetype.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_nas.html b/machines/templates/machines/index_nas.html index b4a3e4fb..5eb4616e 100644 --- a/machines/templates/machines/index_nas.html +++ b/machines/templates/machines/index_nas.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_role.html b/machines/templates/machines/index_role.html index c92bcb25..7384c8e6 100644 --- a/machines/templates/machines/index_role.html +++ b/machines/templates/machines/index_role.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_service.html b/machines/templates/machines/index_service.html index 05376f2b..d28da814 100644 --- a/machines/templates/machines/index_service.html +++ b/machines/templates/machines/index_service.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_sshfp.html b/machines/templates/machines/index_sshfp.html index b3fe7d35..69c981fc 100644 --- a/machines/templates/machines/index_sshfp.html +++ b/machines/templates/machines/index_sshfp.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/index_vlan.html b/machines/templates/machines/index_vlan.html index 1ab87427..67262037 100644 --- a/machines/templates/machines/index_vlan.html +++ b/machines/templates/machines/index_vlan.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/machine.html b/machines/templates/machines/machine.html index 0021caa2..f74730b2 100644 --- a/machines/templates/machines/machine.html +++ b/machines/templates/machines/machine.html @@ -1,6 +1,6 @@ {% extends 'machines/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/templates/machines/sidebar.html b/machines/templates/machines/sidebar.html index 4b0f3392..47f6f460 100644 --- a/machines/templates/machines/sidebar.html +++ b/machines/templates/machines/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/machines/tests.py b/machines/tests.py index 8dc56f18..084219f8 100644 --- a/machines/tests.py +++ b/machines/tests.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/urls.py b/machines/urls.py index 3b7b77b9..a8e1dbea 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/machines/views.py b/machines/views.py index 811bf2a0..8ec85583 100644 --- a/machines/views.py +++ b/machines/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/apps.py b/multi_op/apps.py index 4e28cab6..eeb49009 100644 --- a/multi_op/apps.py +++ b/multi_op/apps.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/forms.py b/multi_op/forms.py index 2de9eac1..59043287 100644 --- a/multi_op/forms.py +++ b/multi_op/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index 43e4e604..efed595a 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/models.py b/multi_op/models.py index cc12d661..4b74df49 100644 --- a/multi_op/models.py +++ b/multi_op/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/preferences/__init__.py b/multi_op/preferences/__init__.py index c73d6113..737e0f26 100644 --- a/multi_op/preferences/__init__.py +++ b/multi_op/preferences/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/preferences/forms.py b/multi_op/preferences/forms.py index 9eed4687..406a9c3b 100644 --- a/multi_op/preferences/forms.py +++ b/multi_op/preferences/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/preferences/models.py b/multi_op/preferences/models.py index 539b0f8a..d4228e8e 100644 --- a/multi_op/preferences/models.py +++ b/multi_op/preferences/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/preferences/views.py b/multi_op/preferences/views.py index fa9509e6..87e34e6d 100644 --- a/multi_op/preferences/views.py +++ b/multi_op/preferences/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/templates/multi_op/aff_room_state.html b/multi_op/templates/multi_op/aff_room_state.html index f58e8148..848d5b67 100644 --- a/multi_op/templates/multi_op/aff_room_state.html +++ b/multi_op/templates/multi_op/aff_room_state.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/multi_op/templates/multi_op/index_room_state.html b/multi_op/templates/multi_op/index_room_state.html index 6e7bb45e..9dccbefb 100644 --- a/multi_op/templates/multi_op/index_room_state.html +++ b/multi_op/templates/multi_op/index_room_state.html @@ -1,6 +1,6 @@ {% extends 'multi_op/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/multi_op/templates/multi_op/sidebar.html b/multi_op/templates/multi_op/sidebar.html index 7de04192..54797f6e 100644 --- a/multi_op/templates/multi_op/sidebar.html +++ b/multi_op/templates/multi_op/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/multi_op/urls.py b/multi_op/urls.py index 45b144e2..3e10c882 100644 --- a/multi_op/urls.py +++ b/multi_op/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/multi_op/views.py b/multi_op/views.py index 01e55aae..b4a559f4 100644 --- a/multi_op/views.py +++ b/multi_op/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/__init__.py b/preferences/__init__.py index 74c32297..4799e4a7 100644 --- a/preferences/__init__.py +++ b/preferences/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/acl.py b/preferences/acl.py index ef647029..b9e5a347 100644 --- a/preferences/acl.py +++ b/preferences/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/admin.py b/preferences/admin.py index d3e4a22d..fc55d93d 100644 --- a/preferences/admin.py +++ b/preferences/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/api/serializers.py b/preferences/api/serializers.py index 04c01c48..47a4a07a 100644 --- a/preferences/api/serializers.py +++ b/preferences/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/api/urls.py b/preferences/api/urls.py index b9ebf859..df0a210c 100644 --- a/preferences/api/urls.py +++ b/preferences/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/api/views.py b/preferences/api/views.py index 6bce71b2..e52766f1 100644 --- a/preferences/api/views.py +++ b/preferences/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/forms.py b/preferences/forms.py index 2457cb38..a0880fd6 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -1,4 +1,4 @@ -# Re2o un logiciel d'administration développé initiallement au rezometz. Il +# Re2o un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index ae69c93f..74e962b8 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/models.py b/preferences/models.py index 4be70fa7..4c196527 100644 --- a/preferences/models.py +++ b/preferences/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o un logiciel d'administration développé initiallement au rezometz. Il +# Re2o un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/templates/preferences/aff_document_template.html b/preferences/templates/preferences/aff_document_template.html index d2f5e577..e435264a 100644 --- a/preferences/templates/preferences/aff_document_template.html +++ b/preferences/templates/preferences/aff_document_template.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_mailcontact.html b/preferences/templates/preferences/aff_mailcontact.html index 2d80e0b1..592dfa71 100644 --- a/preferences/templates/preferences/aff_mailcontact.html +++ b/preferences/templates/preferences/aff_mailcontact.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_mandate.html b/preferences/templates/preferences/aff_mandate.html index 10393200..ed4cb24e 100644 --- a/preferences/templates/preferences/aff_mandate.html +++ b/preferences/templates/preferences/aff_mandate.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_radiusattributes.html b/preferences/templates/preferences/aff_radiusattributes.html index 8edd8b93..7df3a678 100644 --- a/preferences/templates/preferences/aff_radiusattributes.html +++ b/preferences/templates/preferences/aff_radiusattributes.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_radiuskey.html b/preferences/templates/preferences/aff_radiuskey.html index f3b6fc9f..48d7acf6 100644 --- a/preferences/templates/preferences/aff_radiuskey.html +++ b/preferences/templates/preferences/aff_radiuskey.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_radiusoptions.html b/preferences/templates/preferences/aff_radiusoptions.html index fad2dbb4..11405ede 100644 --- a/preferences/templates/preferences/aff_radiusoptions.html +++ b/preferences/templates/preferences/aff_radiusoptions.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_reminder.html b/preferences/templates/preferences/aff_reminder.html index 134b45d5..7f1d1d79 100644 --- a/preferences/templates/preferences/aff_reminder.html +++ b/preferences/templates/preferences/aff_reminder.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_service.html b/preferences/templates/preferences/aff_service.html index baa08feb..3b57211a 100644 --- a/preferences/templates/preferences/aff_service.html +++ b/preferences/templates/preferences/aff_service.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/aff_switchmanagementcred.html b/preferences/templates/preferences/aff_switchmanagementcred.html index 88c680b7..c5607676 100644 --- a/preferences/templates/preferences/aff_switchmanagementcred.html +++ b/preferences/templates/preferences/aff_switchmanagementcred.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/delete.html b/preferences/templates/preferences/delete.html index 227eaf31..8770c474 100644 --- a/preferences/templates/preferences/delete.html +++ b/preferences/templates/preferences/delete.html @@ -1,6 +1,6 @@ {% extends 'preferences/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/display_preferences.html b/preferences/templates/preferences/display_preferences.html index e2de0ee0..c868d573 100644 --- a/preferences/templates/preferences/display_preferences.html +++ b/preferences/templates/preferences/display_preferences.html @@ -1,6 +1,6 @@ {% extends 'preferences/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/edit_preferences.html b/preferences/templates/preferences/edit_preferences.html index b0f70bad..2d9e1a62 100644 --- a/preferences/templates/preferences/edit_preferences.html +++ b/preferences/templates/preferences/edit_preferences.html @@ -1,6 +1,6 @@ {% extends 'preferences/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/preferences.html b/preferences/templates/preferences/preferences.html index 5c76de61..dda6ddfa 100644 --- a/preferences/templates/preferences/preferences.html +++ b/preferences/templates/preferences/preferences.html @@ -1,6 +1,6 @@ {% extends 'preferences/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templates/preferences/sidebar.html b/preferences/templates/preferences/sidebar.html index 7fb0d086..a6c52493 100644 --- a/preferences/templates/preferences/sidebar.html +++ b/preferences/templates/preferences/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/preferences/templatetags/__init__.py b/preferences/templatetags/__init__.py index 7b69b281..97408525 100644 --- a/preferences/templatetags/__init__.py +++ b/preferences/templatetags/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# re2o est un logiciel d'administration développé initiallement au rezometz. Il +# re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/tests.py b/preferences/tests.py index 68ad1667..891dc457 100644 --- a/preferences/tests.py +++ b/preferences/tests.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/urls.py b/preferences/urls.py index 82780cb8..c919a84c 100644 --- a/preferences/urls.py +++ b/preferences/urls.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/preferences/views.py b/preferences/views.py index 129e17ff..077fd105 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/__init__.py b/re2o/__init__.py index aeb27316..88503c08 100644 --- a/re2o/__init__.py +++ b/re2o/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/acl.py b/re2o/acl.py index 00df6d8a..600b839f 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/aes_field.py b/re2o/aes_field.py index f5c98002..156531f7 100644 --- a/re2o/aes_field.py +++ b/re2o/aes_field.py @@ -1,5 +1,5 @@ # coding:utf-8 -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/base.py b/re2o/base.py index c2863643..80f033d8 100644 --- a/re2o/base.py +++ b/re2o/base.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/context_processors.py b/re2o/context_processors.py index 6b61e2a0..c32c263f 100644 --- a/re2o/context_processors.py +++ b/re2o/context_processors.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/field_permissions.py b/re2o/field_permissions.py index b55248b3..a35d206d 100644 --- a/re2o/field_permissions.py +++ b/re2o/field_permissions.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index d54ea9ec..a734d26e 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # @@ -150,7 +150,7 @@ msgstr "Informations supplémentaires" #: re2o/templates/re2o/about.html:57 msgid "" -"Re2o is an administration tool initiated by Rezo Metz and a few members of other FedeRez associations around the summer 2016.
    It is intended to " "be a tool independent from any network infrastructure so it can be setup in " @@ -161,7 +161,7 @@ msgid "" "process, we will be glad to welcome you so do not hesitate to contact us and " "come help us build the future of Re2o." msgstr "" -"Re2o est un outil d'administration initié par Rézo Metz et quelques membres d'autres associations de FedeRez autour de l'été 2016.
    Il se veut " "être un outil indépendant de toute infrastructure réseau pour pouvoir être " diff --git a/re2o/login.py b/re2o/login.py index 1dae9073..a997074b 100644 --- a/re2o/login.py +++ b/re2o/login.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/mail_utils.py b/re2o/mail_utils.py index 2e104ec5..c80f2cfb 100644 --- a/re2o/mail_utils.py +++ b/re2o/mail_utils.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/management/commands/gen_contrib.py b/re2o/management/commands/gen_contrib.py index 430c2bc3..c5d693be 100644 --- a/re2o/management/commands/gen_contrib.py +++ b/re2o/management/commands/gen_contrib.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/middleware.py b/re2o/middleware.py index 5ff3df60..4ea8b5d5 100644 --- a/re2o/middleware.py +++ b/re2o/middleware.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/mixins.py b/re2o/mixins.py index c091a6d9..44cd517e 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/script_utils.py b/re2o/script_utils.py index 667e38d3..12464594 100644 --- a/re2o/script_utils.py +++ b/re2o/script_utils.py @@ -1,5 +1,5 @@ # ⁻*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/settings.py b/re2o/settings.py index 243aa93b..f7975ae4 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/settings_default.py b/re2o/settings_default.py index 115b99a5..d5ceb431 100644 --- a/re2o/settings_default.py +++ b/re2o/settings_default.py @@ -1,5 +1,5 @@ # coding: utf-8 -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/settings_local.example.py b/re2o/settings_local.example.py index 8c9b483c..d557d7eb 100644 --- a/re2o/settings_local.example.py +++ b/re2o/settings_local.example.py @@ -1,5 +1,5 @@ # coding: utf-8 -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templates/re2o/about.html b/re2o/templates/re2o/about.html index b69a8c15..cbe48a13 100644 --- a/re2o/templates/re2o/about.html +++ b/re2o/templates/re2o/about.html @@ -1,6 +1,6 @@ {% extends 're2o/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. @@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "About Re2o" %}

    {% blocktrans trimmed %} Re2o is an administration tool initiated by - Rezo Metz and a few + Rezo Metz and a few members of other FedeRez associations around the summer 2016.
    It is intended to be a tool independent from any network infrastructure diff --git a/re2o/templates/re2o/aff_history.html b/re2o/templates/re2o/aff_history.html index a52ecc38..7eeb39a0 100644 --- a/re2o/templates/re2o/aff_history.html +++ b/re2o/templates/re2o/aff_history.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/re2o/templates/re2o/contact.html b/re2o/templates/re2o/contact.html index bf97c288..598d165b 100644 --- a/re2o/templates/re2o/contact.html +++ b/re2o/templates/re2o/contact.html @@ -1,6 +1,6 @@ {% extends 're2o/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/re2o/templates/re2o/history.html b/re2o/templates/re2o/history.html index 53ccd587..88daf018 100644 --- a/re2o/templates/re2o/history.html +++ b/re2o/templates/re2o/history.html @@ -1,6 +1,6 @@ {% extends 're2o/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/re2o/templates/re2o/index.html b/re2o/templates/re2o/index.html index 89a50489..d1c2dad2 100644 --- a/re2o/templates/re2o/index.html +++ b/re2o/templates/re2o/index.html @@ -1,6 +1,6 @@ {% extends 're2o/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/re2o/templates/re2o/sidebar.html b/re2o/templates/re2o/sidebar.html index 75f23c03..0ca15273 100644 --- a/re2o/templates/re2o/sidebar.html +++ b/re2o/templates/re2o/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/re2o/templatetags/__init__.py b/re2o/templatetags/__init__.py index cff50933..2316fc22 100644 --- a/re2o/templatetags/__init__.py +++ b/re2o/templatetags/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index f34dfb7f..132239be 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/design.py b/re2o/templatetags/design.py index 57699f3b..488b1489 100644 --- a/re2o/templatetags/design.py +++ b/re2o/templatetags/design.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# re2o est un logiciel d'administration développé initiallement au rezometz. Il +# re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/massive_bootstrap_form.py b/re2o/templatetags/massive_bootstrap_form.py index 12b8ec93..449f7c24 100644 --- a/re2o/templatetags/massive_bootstrap_form.py +++ b/re2o/templatetags/massive_bootstrap_form.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/pagination_extra.py b/re2o/templatetags/pagination_extra.py index 41491731..eb50a90d 100644 --- a/re2o/templatetags/pagination_extra.py +++ b/re2o/templatetags/pagination_extra.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/self_adhesion.py b/re2o/templatetags/self_adhesion.py index 315e4728..31f8ccc9 100644 --- a/re2o/templatetags/self_adhesion.py +++ b/re2o/templatetags/self_adhesion.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/templatetags/url_insert_param.py b/re2o/templatetags/url_insert_param.py index 5ead54a5..beeee15b 100644 --- a/re2o/templatetags/url_insert_param.py +++ b/re2o/templatetags/url_insert_param.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/urls.py b/re2o/urls.py index 146fc3b9..0942ad48 100644 --- a/re2o/urls.py +++ b/re2o/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/utils.py b/re2o/utils.py index 00c99df1..b7a0cd23 100644 --- a/re2o/utils.py +++ b/re2o/utils.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/views.py b/re2o/views.py index 42e8cebf..51f36b1a 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/re2o/wsgi.py b/re2o/wsgi.py index 968298ed..4f8d2196 100644 --- a/re2o/wsgi.py +++ b/re2o/wsgi.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/__init__.py b/search/__init__.py index 93f634cc..3a98fd7f 100644 --- a/search/__init__.py +++ b/search/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/acl.py b/search/acl.py index d85914f9..f075cc6e 100644 --- a/search/acl.py +++ b/search/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/admin.py b/search/admin.py index b4cf317a..619b20c9 100644 --- a/search/admin.py +++ b/search/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/engine.py b/search/engine.py index c1adc501..dc4eede8 100644 --- a/search/engine.py +++ b/search/engine.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/forms.py b/search/forms.py index ef049ffc..35151a3a 100644 --- a/search/forms.py +++ b/search/forms.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 7e93001b..353e0ad6 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/templates/search/index.html b/search/templates/search/index.html index e560ccaa..72c4bf77 100644 --- a/search/templates/search/index.html +++ b/search/templates/search/index.html @@ -1,6 +1,6 @@ {% extends 'search/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/search/templates/search/search.html b/search/templates/search/search.html index d1a9d5f8..2214b9a2 100644 --- a/search/templates/search/search.html +++ b/search/templates/search/search.html @@ -1,6 +1,6 @@ {% extends 'search/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/search/templates/search/sidebar.html b/search/templates/search/sidebar.html index 717a46b1..663dbd62 100644 --- a/search/templates/search/sidebar.html +++ b/search/templates/search/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/search/tests.py b/search/tests.py index 3f0410dd..9528320f 100644 --- a/search/tests.py +++ b/search/tests.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/urls.py b/search/urls.py index a79e8391..72778cfd 100644 --- a/search/urls.py +++ b/search/urls.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/search/views.py b/search/views.py index 6f71000c..afb6065e 100644 --- a/search/views.py +++ b/search/views.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/static/js/collapse-from-url.js b/static/js/collapse-from-url.js index 6c85762b..0c3f0c3a 100644 --- a/static/js/collapse-from-url.js +++ b/static/js/collapse-from-url.js @@ -1,4 +1,4 @@ -// Re2o est un logiciel d'administration développé initiallement au rezometz. Il +// Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il // se veut agnostique au réseau considéré, de manière à être installable en // quelques clics. // diff --git a/static/js/sapphire.js b/static/js/sapphire.js index 4f2e6c29..57a57e9d 100644 --- a/static/js/sapphire.js +++ b/static/js/sapphire.js @@ -1,4 +1,4 @@ -// Re2o est un logiciel d'administration développé initiallement au rezometz. Il +// Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il // se veut agnostique au réseau considéré, de manière à être installable en // quelques clics. // diff --git a/templates/base.html b/templates/base.html index f4692e1b..be46cccb 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/add.html b/templates/buttons/add.html index b04381ca..17100a81 100644 --- a/templates/buttons/add.html +++ b/templates/buttons/add.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/edit.html b/templates/buttons/edit.html index c7bbaac4..0ae09a79 100644 --- a/templates/buttons/edit.html +++ b/templates/buttons/edit.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/history.html b/templates/buttons/history.html index 71c2c71d..5f0dfd21 100644 --- a/templates/buttons/history.html +++ b/templates/buttons/history.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/multiple_checkbox_alt.html b/templates/buttons/multiple_checkbox_alt.html index 2e59845f..9cf1b04e 100644 --- a/templates/buttons/multiple_checkbox_alt.html +++ b/templates/buttons/multiple_checkbox_alt.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/setlang.html b/templates/buttons/setlang.html index e6a61da8..be0c82db 100644 --- a/templates/buttons/setlang.html +++ b/templates/buttons/setlang.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/sort.html b/templates/buttons/sort.html index 2f34a2c6..04c6e043 100644 --- a/templates/buttons/sort.html +++ b/templates/buttons/sort.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/suppr.html b/templates/buttons/suppr.html index 1cb6cbe4..bb3eb18f 100644 --- a/templates/buttons/suppr.html +++ b/templates/buttons/suppr.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/buttons/view.html b/templates/buttons/view.html index dc0df74a..3e6b62ec 100644 --- a/templates/buttons/view.html +++ b/templates/buttons/view.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/errors/404.html b/templates/errors/404.html index 2f743532..71e54624 100644 --- a/templates/errors/404.html +++ b/templates/errors/404.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/footer.html b/templates/footer.html index 25990629..c0415ba1 100644 --- a/templates/footer.html +++ b/templates/footer.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 2dc1dfd3..5c6c4af0 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/templates/nav.html b/templates/nav.html index b48c97c8..c7cc394f 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/pagination.html b/templates/pagination.html index 402f61be..d19de8b1 100644 --- a/templates/pagination.html +++ b/templates/pagination.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/registration/login.html b/templates/registration/login.html index 47e8b25f..d27c5f70 100644 --- a/templates/registration/login.html +++ b/templates/registration/login.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/templates/sidebar.html b/templates/sidebar.html index 08600b30..5c14f419 100644 --- a/templates/sidebar.html +++ b/templates/sidebar.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/test_utils/runner.py b/test_utils/runner.py index 6cdd4cae..888e8a02 100644 --- a/test_utils/runner.py +++ b/test_utils/runner.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/admin.py b/tickets/admin.py index 1644ee75..620fc998 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/forms.py b/tickets/forms.py index 86f1a953..e28bfa85 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index c1f15f79..e6d840d4 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/models.py b/tickets/models.py index d493d09b..3bae040a 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/preferences/__init__.py b/tickets/preferences/__init__.py index 53b6e42e..2147642b 100644 --- a/tickets/preferences/__init__.py +++ b/tickets/preferences/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/preferences/forms.py b/tickets/preferences/forms.py index 4d3f16d3..ff76f1d9 100644 --- a/tickets/preferences/forms.py +++ b/tickets/preferences/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/preferences/models.py b/tickets/preferences/models.py index 221c04a6..67f8435a 100644 --- a/tickets/preferences/models.py +++ b/tickets/preferences/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/preferences/views.py b/tickets/preferences/views.py index 732a7cbe..e1549fd2 100644 --- a/tickets/preferences/views.py +++ b/tickets/preferences/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/templates/tickets/aff_ticket.html b/tickets/templates/tickets/aff_ticket.html index da7d0c39..c5ad6cda 100644 --- a/tickets/templates/tickets/aff_ticket.html +++ b/tickets/templates/tickets/aff_ticket.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/tickets/templates/tickets/aff_tickets.html b/tickets/templates/tickets/aff_tickets.html index 218c6f0d..beaadc4f 100644 --- a/tickets/templates/tickets/aff_tickets.html +++ b/tickets/templates/tickets/aff_tickets.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/tickets/templates/tickets/delete.html b/tickets/templates/tickets/delete.html index c583ee5b..906fc886 100644 --- a/tickets/templates/tickets/delete.html +++ b/tickets/templates/tickets/delete.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/tickets/templates/tickets/edit.html b/tickets/templates/tickets/edit.html index 29725fc4..3c5c1c3f 100644 --- a/tickets/templates/tickets/edit.html +++ b/tickets/templates/tickets/edit.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/tickets/templates/tickets/index.html b/tickets/templates/tickets/index.html index 80b11351..e748ffa0 100644 --- a/tickets/templates/tickets/index.html +++ b/tickets/templates/tickets/index.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/tickets/urls.py b/tickets/urls.py index fe0b88ec..1fcab4c5 100644 --- a/tickets/urls.py +++ b/tickets/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/tickets/views.py b/tickets/views.py index a87285ae..f0736265 100644 --- a/tickets/views.py +++ b/tickets/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/__init__.py b/topologie/__init__.py index 2f5cebcd..4fc592f6 100644 --- a/topologie/__init__.py +++ b/topologie/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/acl.py b/topologie/acl.py index c17073d0..824a0df5 100644 --- a/topologie/acl.py +++ b/topologie/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/admin.py b/topologie/admin.py index bd5a61c4..514af42d 100644 --- a/topologie/admin.py +++ b/topologie/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/api/serializers.py b/topologie/api/serializers.py index b11a7ffa..0dd9f599 100644 --- a/topologie/api/serializers.py +++ b/topologie/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/api/urls.py b/topologie/api/urls.py index 1cfd2d3e..4c5e859a 100644 --- a/topologie/api/urls.py +++ b/topologie/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/api/views.py b/topologie/api/views.py index 4cdd5470..a84eda5f 100644 --- a/topologie/api/views.py +++ b/topologie/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/forms.py b/topologie/forms.py index 205be5fb..4044ca3e 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index d9f09aff..e99b290d 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0001_initial.py b/topologie/migrations/0001_initial.py index 5f66ecdb..8dd05d98 100644 --- a/topologie/migrations/0001_initial.py +++ b/topologie/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0002_auto_20160703_1118.py b/topologie/migrations/0002_auto_20160703_1118.py index cae34d32..8f5bf6a0 100644 --- a/topologie/migrations/0002_auto_20160703_1118.py +++ b/topologie/migrations/0002_auto_20160703_1118.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0003_room.py b/topologie/migrations/0003_room.py index 8d476fcd..0a566be4 100644 --- a/topologie/migrations/0003_room.py +++ b/topologie/migrations/0003_room.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0004_auto_20160703_1122.py b/topologie/migrations/0004_auto_20160703_1122.py index 5b8a6038..1215cd28 100644 --- a/topologie/migrations/0004_auto_20160703_1122.py +++ b/topologie/migrations/0004_auto_20160703_1122.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0005_auto_20160703_1123.py b/topologie/migrations/0005_auto_20160703_1123.py index 78510538..dccdea29 100644 --- a/topologie/migrations/0005_auto_20160703_1123.py +++ b/topologie/migrations/0005_auto_20160703_1123.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0006_auto_20160703_1129.py b/topologie/migrations/0006_auto_20160703_1129.py index df20974c..00f4f280 100644 --- a/topologie/migrations/0006_auto_20160703_1129.py +++ b/topologie/migrations/0006_auto_20160703_1129.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0007_auto_20160703_1148.py b/topologie/migrations/0007_auto_20160703_1148.py index df01f4fa..37226fb1 100644 --- a/topologie/migrations/0007_auto_20160703_1148.py +++ b/topologie/migrations/0007_auto_20160703_1148.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0008_port_room.py b/topologie/migrations/0008_port_room.py index 5dba66aa..58891714 100644 --- a/topologie/migrations/0008_port_room.py +++ b/topologie/migrations/0008_port_room.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0009_auto_20160703_1200.py b/topologie/migrations/0009_auto_20160703_1200.py index 6db065f2..6252c979 100644 --- a/topologie/migrations/0009_auto_20160703_1200.py +++ b/topologie/migrations/0009_auto_20160703_1200.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0010_auto_20160704_2148.py b/topologie/migrations/0010_auto_20160704_2148.py index 5e51a5da..6c2d4b3f 100644 --- a/topologie/migrations/0010_auto_20160704_2148.py +++ b/topologie/migrations/0010_auto_20160704_2148.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0011_auto_20160704_2153.py b/topologie/migrations/0011_auto_20160704_2153.py index 18caec08..be495ad4 100644 --- a/topologie/migrations/0011_auto_20160704_2153.py +++ b/topologie/migrations/0011_auto_20160704_2153.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0012_port_machine_interface.py b/topologie/migrations/0012_port_machine_interface.py index e9518085..b720a1ba 100644 --- a/topologie/migrations/0012_port_machine_interface.py +++ b/topologie/migrations/0012_port_machine_interface.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0013_port_related.py b/topologie/migrations/0013_port_related.py index 057dc48c..decbeca9 100644 --- a/topologie/migrations/0013_port_related.py +++ b/topologie/migrations/0013_port_related.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0014_auto_20160706_1238.py b/topologie/migrations/0014_auto_20160706_1238.py index 9a3f44e8..8649f5cd 100644 --- a/topologie/migrations/0014_auto_20160706_1238.py +++ b/topologie/migrations/0014_auto_20160706_1238.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0015_auto_20160706_1452.py b/topologie/migrations/0015_auto_20160706_1452.py index dc9c8c4b..4f00811c 100644 --- a/topologie/migrations/0015_auto_20160706_1452.py +++ b/topologie/migrations/0015_auto_20160706_1452.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0016_auto_20160706_1531.py b/topologie/migrations/0016_auto_20160706_1531.py index dcbed85e..3298e753 100644 --- a/topologie/migrations/0016_auto_20160706_1531.py +++ b/topologie/migrations/0016_auto_20160706_1531.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0017_auto_20160718_1141.py b/topologie/migrations/0017_auto_20160718_1141.py index 0d56607a..4ed5ee76 100644 --- a/topologie/migrations/0017_auto_20160718_1141.py +++ b/topologie/migrations/0017_auto_20160718_1141.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0018_room_details.py b/topologie/migrations/0018_room_details.py index fcb64901..8c7b5f80 100644 --- a/topologie/migrations/0018_room_details.py +++ b/topologie/migrations/0018_room_details.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0019_auto_20161026_1348.py b/topologie/migrations/0019_auto_20161026_1348.py index 9000cc64..310629fc 100644 --- a/topologie/migrations/0019_auto_20161026_1348.py +++ b/topologie/migrations/0019_auto_20161026_1348.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0020_auto_20161119_0033.py b/topologie/migrations/0020_auto_20161119_0033.py index f6714a0b..6f2db9cc 100644 --- a/topologie/migrations/0020_auto_20161119_0033.py +++ b/topologie/migrations/0020_auto_20161119_0033.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0021_port_radius.py b/topologie/migrations/0021_port_radius.py index db0a401f..7f56ed13 100644 --- a/topologie/migrations/0021_port_radius.py +++ b/topologie/migrations/0021_port_radius.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/0022_auto_20161211_1622.py b/topologie/migrations/0022_auto_20161211_1622.py index 52e0fcbb..20e196be 100644 --- a/topologie/migrations/0022_auto_20161211_1622.py +++ b/topologie/migrations/0022_auto_20161211_1622.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/migrations/__init__.py b/topologie/migrations/__init__.py index b409e525..fecd9684 100644 --- a/topologie/migrations/__init__.py +++ b/topologie/migrations/__init__.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/models.py b/topologie/models.py index ef93c899..c0362480 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/templates/topologie/aff_ap.html b/topologie/templates/topologie/aff_ap.html index afbf00ca..b4ad9e3f 100644 --- a/topologie/templates/topologie/aff_ap.html +++ b/topologie/templates/topologie/aff_ap.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_building.html b/topologie/templates/topologie/aff_building.html index cf288b53..59322aed 100644 --- a/topologie/templates/topologie/aff_building.html +++ b/topologie/templates/topologie/aff_building.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_chambres.html b/topologie/templates/topologie/aff_chambres.html index d03f7b19..d0308a42 100644 --- a/topologie/templates/topologie/aff_chambres.html +++ b/topologie/templates/topologie/aff_chambres.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_constructor_switch.html b/topologie/templates/topologie/aff_constructor_switch.html index 9e4dcb42..0f69a1d3 100644 --- a/topologie/templates/topologie/aff_constructor_switch.html +++ b/topologie/templates/topologie/aff_constructor_switch.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_dormitory.html b/topologie/templates/topologie/aff_dormitory.html index b4311b8a..205fc1b6 100644 --- a/topologie/templates/topologie/aff_dormitory.html +++ b/topologie/templates/topologie/aff_dormitory.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_model_switch.html b/topologie/templates/topologie/aff_model_switch.html index 51f98617..847a0e4a 100644 --- a/topologie/templates/topologie/aff_model_switch.html +++ b/topologie/templates/topologie/aff_model_switch.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_modules.html b/topologie/templates/topologie/aff_modules.html index ac09ec13..d0962f48 100644 --- a/topologie/templates/topologie/aff_modules.html +++ b/topologie/templates/topologie/aff_modules.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_port.html b/topologie/templates/topologie/aff_port.html index 91742147..6ed9d6c0 100644 --- a/topologie/templates/topologie/aff_port.html +++ b/topologie/templates/topologie/aff_port.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_port_profile.html b/topologie/templates/topologie/aff_port_profile.html index a1b6b6a0..7fe7d145 100644 --- a/topologie/templates/topologie/aff_port_profile.html +++ b/topologie/templates/topologie/aff_port_profile.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_repr_switch.html b/topologie/templates/topologie/aff_repr_switch.html index f0924124..02cc592a 100644 --- a/topologie/templates/topologie/aff_repr_switch.html +++ b/topologie/templates/topologie/aff_repr_switch.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_stacks.html b/topologie/templates/topologie/aff_stacks.html index 1ca259eb..46def501 100644 --- a/topologie/templates/topologie/aff_stacks.html +++ b/topologie/templates/topologie/aff_stacks.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_switch.html b/topologie/templates/topologie/aff_switch.html index e75d41a2..4a532f79 100644 --- a/topologie/templates/topologie/aff_switch.html +++ b/topologie/templates/topologie/aff_switch.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_switch_bay.html b/topologie/templates/topologie/aff_switch_bay.html index 9335f0a8..33f7b478 100644 --- a/topologie/templates/topologie/aff_switch_bay.html +++ b/topologie/templates/topologie/aff_switch_bay.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/aff_vlanoptions.html b/topologie/templates/topologie/aff_vlanoptions.html index 65c80e34..2fcbc6f5 100644 --- a/topologie/templates/topologie/aff_vlanoptions.html +++ b/topologie/templates/topologie/aff_vlanoptions.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/delete.html b/topologie/templates/topologie/delete.html index e5dea3c4..e055bf0e 100644 --- a/topologie/templates/topologie/delete.html +++ b/topologie/templates/topologie/delete.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/edit_stack_sw.html b/topologie/templates/topologie/edit_stack_sw.html index 6e1cf848..f5e3a293 100644 --- a/topologie/templates/topologie/edit_stack_sw.html +++ b/topologie/templates/topologie/edit_stack_sw.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index.html b/topologie/templates/topologie/index.html index 85424b25..698a3d3f 100644 --- a/topologie/templates/topologie/index.html +++ b/topologie/templates/topologie/index.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_ap.html b/topologie/templates/topologie/index_ap.html index b1521937..588f4c85 100644 --- a/topologie/templates/topologie/index_ap.html +++ b/topologie/templates/topologie/index_ap.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_building.html b/topologie/templates/topologie/index_building.html index d653a032..ef17f85b 100644 --- a/topologie/templates/topologie/index_building.html +++ b/topologie/templates/topologie/index_building.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_dormitory.html b/topologie/templates/topologie/index_dormitory.html index c137bb70..0a500528 100644 --- a/topologie/templates/topologie/index_dormitory.html +++ b/topologie/templates/topologie/index_dormitory.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_model_switch.html b/topologie/templates/topologie/index_model_switch.html index da82adb7..08e40247 100644 --- a/topologie/templates/topologie/index_model_switch.html +++ b/topologie/templates/topologie/index_model_switch.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_module.html b/topologie/templates/topologie/index_module.html index fae9b282..2199a8a6 100644 --- a/topologie/templates/topologie/index_module.html +++ b/topologie/templates/topologie/index_module.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_p.html b/topologie/templates/topologie/index_p.html index 462bd146..5e845ef2 100644 --- a/topologie/templates/topologie/index_p.html +++ b/topologie/templates/topologie/index_p.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_portprofile.html b/topologie/templates/topologie/index_portprofile.html index dd04687d..006b8491 100644 --- a/topologie/templates/topologie/index_portprofile.html +++ b/topologie/templates/topologie/index_portprofile.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_room.html b/topologie/templates/topologie/index_room.html index 17658e0c..629feffa 100644 --- a/topologie/templates/topologie/index_room.html +++ b/topologie/templates/topologie/index_room.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_stack.html b/topologie/templates/topologie/index_stack.html index 6006167f..1745876b 100644 --- a/topologie/templates/topologie/index_stack.html +++ b/topologie/templates/topologie/index_stack.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/index_switch_bay.html b/topologie/templates/topologie/index_switch_bay.html index f3613e48..787b7e0a 100644 --- a/topologie/templates/topologie/index_switch_bay.html +++ b/topologie/templates/topologie/index_switch_bay.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/sidebar.html b/topologie/templates/topologie/sidebar.html index 751fff3c..3532773b 100644 --- a/topologie/templates/topologie/sidebar.html +++ b/topologie/templates/topologie/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/switch.html b/topologie/templates/topologie/switch.html index bdc0dae0..6af65bcd 100644 --- a/topologie/templates/topologie/switch.html +++ b/topologie/templates/topologie/switch.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/topo.html b/topologie/templates/topologie/topo.html index 409b65ec..900a2b59 100644 --- a/topologie/templates/topologie/topo.html +++ b/topologie/templates/topologie/topo.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/templates/topologie/topo_more.html b/topologie/templates/topologie/topo_more.html index 617870d7..0180c72f 100644 --- a/topologie/templates/topologie/topo_more.html +++ b/topologie/templates/topologie/topo_more.html @@ -1,6 +1,6 @@ {% extends 'topologie/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/topologie/tests.py b/topologie/tests.py index 42d5ddfd..330da395 100644 --- a/topologie/tests.py +++ b/topologie/tests.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/urls.py b/topologie/urls.py index f387c226..9a0ff1e3 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/topologie/views.py b/topologie/views.py index 06eaf410..ff4db520 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/__init__.py b/users/__init__.py index b75d67cb..9dce8a7b 100644 --- a/users/__init__.py +++ b/users/__init__.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/acl.py b/users/acl.py index 6dce7807..facf11cf 100644 --- a/users/acl.py +++ b/users/acl.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/admin.py b/users/admin.py index e00f8e1f..a18dae90 100644 --- a/users/admin.py +++ b/users/admin.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/api/serializers.py b/users/api/serializers.py index fc16a702..4b77446a 100644 --- a/users/api/serializers.py +++ b/users/api/serializers.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/api/urls.py b/users/api/urls.py index ef2b01b1..13598746 100644 --- a/users/api/urls.py +++ b/users/api/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/api/views.py b/users/api/views.py index 57571f70..e9c57b8a 100644 --- a/users/api/views.py +++ b/users/api/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/forms.py b/users/forms.py index d1cdbc4c..1fc00584 100644 --- a/users/forms.py +++ b/users/forms.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 5e5474b0..0c843b6c 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/management/commands/archive.py b/users/management/commands/archive.py index 1e4601a0..9840929e 100644 --- a/users/management/commands/archive.py +++ b/users/management/commands/archive.py @@ -1,5 +1,5 @@ # ⁻*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/management/commands/chgpass.py b/users/management/commands/chgpass.py index 2937bb3c..5de9aa8d 100644 --- a/users/management/commands/chgpass.py +++ b/users/management/commands/chgpass.py @@ -1,5 +1,5 @@ # ⁻*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/management/commands/chsh.py b/users/management/commands/chsh.py index 95be7b44..0b815543 100644 --- a/users/management/commands/chsh.py +++ b/users/management/commands/chsh.py @@ -1,5 +1,5 @@ # ⁻*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/management/commands/derniere_connexion.py b/users/management/commands/derniere_connexion.py index 38352640..9d753809 100644 --- a/users/management/commands/derniere_connexion.py +++ b/users/management/commands/derniere_connexion.py @@ -1,5 +1,5 @@ # ⁻*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py index b10f2bd3..f7a2aa9b 100644 --- a/users/migrations/0001_initial.py +++ b/users/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0002_auto_20160630_2301.py b/users/migrations/0002_auto_20160630_2301.py index 7994825e..766a8523 100644 --- a/users/migrations/0002_auto_20160630_2301.py +++ b/users/migrations/0002_auto_20160630_2301.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0003_listrights_rights.py b/users/migrations/0003_listrights_rights.py index a67a0f4d..29c07d99 100644 --- a/users/migrations/0003_listrights_rights.py +++ b/users/migrations/0003_listrights_rights.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0004_auto_20160701_2312.py b/users/migrations/0004_auto_20160701_2312.py index e925c6df..f7c55ca3 100644 --- a/users/migrations/0004_auto_20160701_2312.py +++ b/users/migrations/0004_auto_20160701_2312.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0005_auto_20160702_0006.py b/users/migrations/0005_auto_20160702_0006.py index e45cc6cf..33515727 100644 --- a/users/migrations/0005_auto_20160702_0006.py +++ b/users/migrations/0005_auto_20160702_0006.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0006_ban.py b/users/migrations/0006_ban.py index d47c33c0..1b9de646 100644 --- a/users/migrations/0006_ban.py +++ b/users/migrations/0006_ban.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0007_auto_20160702_2322.py b/users/migrations/0007_auto_20160702_2322.py index 45e1901e..b24f6fe3 100644 --- a/users/migrations/0007_auto_20160702_2322.py +++ b/users/migrations/0007_auto_20160702_2322.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0008_user_registered.py b/users/migrations/0008_user_registered.py index 36bd4b6b..46263703 100644 --- a/users/migrations/0008_user_registered.py +++ b/users/migrations/0008_user_registered.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0009_user_room.py b/users/migrations/0009_user_room.py index e03c3119..f5a11f84 100644 --- a/users/migrations/0009_user_room.py +++ b/users/migrations/0009_user_room.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0010_auto_20160703_1226.py b/users/migrations/0010_auto_20160703_1226.py index 80b0153a..12fc1c40 100644 --- a/users/migrations/0010_auto_20160703_1226.py +++ b/users/migrations/0010_auto_20160703_1226.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0011_auto_20160703_1227.py b/users/migrations/0011_auto_20160703_1227.py index a064dfe7..e7784a4f 100644 --- a/users/migrations/0011_auto_20160703_1227.py +++ b/users/migrations/0011_auto_20160703_1227.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0012_auto_20160703_1230.py b/users/migrations/0012_auto_20160703_1230.py index 9a9cdcf6..984a0dce 100644 --- a/users/migrations/0012_auto_20160703_1230.py +++ b/users/migrations/0012_auto_20160703_1230.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0013_auto_20160704_1547.py b/users/migrations/0013_auto_20160704_1547.py index adfbf928..c99c31be 100644 --- a/users/migrations/0013_auto_20160704_1547.py +++ b/users/migrations/0013_auto_20160704_1547.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0014_auto_20160704_1548.py b/users/migrations/0014_auto_20160704_1548.py index 72dd09ef..5b0e99b4 100644 --- a/users/migrations/0014_auto_20160704_1548.py +++ b/users/migrations/0014_auto_20160704_1548.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0015_whitelist.py b/users/migrations/0015_whitelist.py index 6d582050..72c59d34 100644 --- a/users/migrations/0015_whitelist.py +++ b/users/migrations/0015_whitelist.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0016_auto_20160706_1220.py b/users/migrations/0016_auto_20160706_1220.py index c3596986..874f3231 100644 --- a/users/migrations/0016_auto_20160706_1220.py +++ b/users/migrations/0016_auto_20160706_1220.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0017_auto_20160707_0105.py b/users/migrations/0017_auto_20160707_0105.py index 2ce1d48e..a0e99bb9 100644 --- a/users/migrations/0017_auto_20160707_0105.py +++ b/users/migrations/0017_auto_20160707_0105.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0018_auto_20160707_0115.py b/users/migrations/0018_auto_20160707_0115.py index 39e72a2c..0d0f8ac4 100644 --- a/users/migrations/0018_auto_20160707_0115.py +++ b/users/migrations/0018_auto_20160707_0115.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0019_auto_20160708_1633.py b/users/migrations/0019_auto_20160708_1633.py index e4cbdbd8..2bdb8eaa 100644 --- a/users/migrations/0019_auto_20160708_1633.py +++ b/users/migrations/0019_auto_20160708_1633.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0020_request.py b/users/migrations/0020_request.py index 6d059c6a..0c0effad 100644 --- a/users/migrations/0020_request.py +++ b/users/migrations/0020_request.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0021_ldapuser.py b/users/migrations/0021_ldapuser.py index 1d475027..7ba729b5 100644 --- a/users/migrations/0021_ldapuser.py +++ b/users/migrations/0021_ldapuser.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0022_ldapuser_sambasid.py b/users/migrations/0022_ldapuser_sambasid.py index 18d214bd..71f23bb5 100644 --- a/users/migrations/0022_ldapuser_sambasid.py +++ b/users/migrations/0022_ldapuser_sambasid.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0023_auto_20160724_1908.py b/users/migrations/0023_auto_20160724_1908.py index 3e9baf2c..7fbf6c60 100644 --- a/users/migrations/0023_auto_20160724_1908.py +++ b/users/migrations/0023_auto_20160724_1908.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0024_remove_ldapuser_mac_list.py b/users/migrations/0024_remove_ldapuser_mac_list.py index 72f3b11a..57c4a53c 100644 --- a/users/migrations/0024_remove_ldapuser_mac_list.py +++ b/users/migrations/0024_remove_ldapuser_mac_list.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0025_listshell.py b/users/migrations/0025_listshell.py index e3a8f7bd..3106e4f1 100644 --- a/users/migrations/0025_listshell.py +++ b/users/migrations/0025_listshell.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0026_user_shell.py b/users/migrations/0026_user_shell.py index c7f14040..9b995993 100644 --- a/users/migrations/0026_user_shell.py +++ b/users/migrations/0026_user_shell.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0027_auto_20160726_0216.py b/users/migrations/0027_auto_20160726_0216.py index 0d09063b..56938bb7 100644 --- a/users/migrations/0027_auto_20160726_0216.py +++ b/users/migrations/0027_auto_20160726_0216.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0028_auto_20160726_0227.py b/users/migrations/0028_auto_20160726_0227.py index 5191e228..ce85ac01 100644 --- a/users/migrations/0028_auto_20160726_0227.py +++ b/users/migrations/0028_auto_20160726_0227.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0029_auto_20160726_0229.py b/users/migrations/0029_auto_20160726_0229.py index 1abd06b2..7213f921 100644 --- a/users/migrations/0029_auto_20160726_0229.py +++ b/users/migrations/0029_auto_20160726_0229.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0030_auto_20160726_0357.py b/users/migrations/0030_auto_20160726_0357.py index 9ccca318..97f17ed6 100644 --- a/users/migrations/0030_auto_20160726_0357.py +++ b/users/migrations/0030_auto_20160726_0357.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0031_auto_20160726_0359.py b/users/migrations/0031_auto_20160726_0359.py index 5cf65e41..064a48db 100644 --- a/users/migrations/0031_auto_20160726_0359.py +++ b/users/migrations/0031_auto_20160726_0359.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0032_auto_20160727_2122.py b/users/migrations/0032_auto_20160727_2122.py index 1dc27579..69d14e3e 100644 --- a/users/migrations/0032_auto_20160727_2122.py +++ b/users/migrations/0032_auto_20160727_2122.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0033_remove_ldapuser_loginshell.py b/users/migrations/0033_remove_ldapuser_loginshell.py index 70be687d..34a15e39 100644 --- a/users/migrations/0033_remove_ldapuser_loginshell.py +++ b/users/migrations/0033_remove_ldapuser_loginshell.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0034_auto_20161018_0037.py b/users/migrations/0034_auto_20161018_0037.py index ed0fbab0..428f6d50 100644 --- a/users/migrations/0034_auto_20161018_0037.py +++ b/users/migrations/0034_auto_20161018_0037.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0035_auto_20161018_0046.py b/users/migrations/0035_auto_20161018_0046.py index ab27d7e2..2dfc9e41 100644 --- a/users/migrations/0035_auto_20161018_0046.py +++ b/users/migrations/0035_auto_20161018_0046.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0036_auto_20161022_2146.py b/users/migrations/0036_auto_20161022_2146.py index 7d7a4521..d053df2c 100644 --- a/users/migrations/0036_auto_20161022_2146.py +++ b/users/migrations/0036_auto_20161022_2146.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0037_auto_20161028_1906.py b/users/migrations/0037_auto_20161028_1906.py index 3e2c3887..cb60be1c 100644 --- a/users/migrations/0037_auto_20161028_1906.py +++ b/users/migrations/0037_auto_20161028_1906.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0038_auto_20161031_0258.py b/users/migrations/0038_auto_20161031_0258.py index bb3f9f86..14d6ff72 100644 --- a/users/migrations/0038_auto_20161031_0258.py +++ b/users/migrations/0038_auto_20161031_0258.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0039_auto_20161119_0033.py b/users/migrations/0039_auto_20161119_0033.py index 9c08ca33..c9eaf3e4 100644 --- a/users/migrations/0039_auto_20161119_0033.py +++ b/users/migrations/0039_auto_20161119_0033.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0040_auto_20161119_1709.py b/users/migrations/0040_auto_20161119_1709.py index d1bd88ab..9fd63098 100644 --- a/users/migrations/0040_auto_20161119_1709.py +++ b/users/migrations/0040_auto_20161119_1709.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0041_listright_details.py b/users/migrations/0041_listright_details.py index a28b246c..19c6506a 100644 --- a/users/migrations/0041_listright_details.py +++ b/users/migrations/0041_listright_details.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/0042_auto_20161126_2028.py b/users/migrations/0042_auto_20161126_2028.py index 491ecec3..3c616ccd 100644 --- a/users/migrations/0042_auto_20161126_2028.py +++ b/users/migrations/0042_auto_20161126_2028.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/migrations/__init__.py b/users/migrations/__init__.py index b409e525..fecd9684 100644 --- a/users/migrations/__init__.py +++ b/users/migrations/__init__.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/models.py b/users/models.py index fac7c2a6..67560472 100755 --- a/users/models.py +++ b/users/models.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. # Il se veut agnostique au réseau considéré, de manière à être installable # en quelques clics. # diff --git a/users/templates/users/aff_bans.html b/users/templates/users/aff_bans.html index f4f973f7..19f43deb 100644 --- a/users/templates/users/aff_bans.html +++ b/users/templates/users/aff_bans.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_clubs.html b/users/templates/users/aff_clubs.html index e4ea9857..9d184ac0 100644 --- a/users/templates/users/aff_clubs.html +++ b/users/templates/users/aff_clubs.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_emailaddress.html b/users/templates/users/aff_emailaddress.html index 9bd3c8d0..aa105f05 100644 --- a/users/templates/users/aff_emailaddress.html +++ b/users/templates/users/aff_emailaddress.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_listright.html b/users/templates/users/aff_listright.html index 489a713a..40cb8a51 100644 --- a/users/templates/users/aff_listright.html +++ b/users/templates/users/aff_listright.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_rights.html b/users/templates/users/aff_rights.html index bb3bdec9..b3afb48a 100644 --- a/users/templates/users/aff_rights.html +++ b/users/templates/users/aff_rights.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_schools.html b/users/templates/users/aff_schools.html index 586c657c..6b6ba78c 100644 --- a/users/templates/users/aff_schools.html +++ b/users/templates/users/aff_schools.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_serviceusers.html b/users/templates/users/aff_serviceusers.html index 68de1d99..6a1c64e5 100644 --- a/users/templates/users/aff_serviceusers.html +++ b/users/templates/users/aff_serviceusers.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_shell.html b/users/templates/users/aff_shell.html index e95d79f0..dab99a39 100644 --- a/users/templates/users/aff_shell.html +++ b/users/templates/users/aff_shell.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_users.html b/users/templates/users/aff_users.html index d0704167..85d567e5 100644 --- a/users/templates/users/aff_users.html +++ b/users/templates/users/aff_users.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/aff_whitelists.html b/users/templates/users/aff_whitelists.html index e93f3ecf..fdfd0b76 100644 --- a/users/templates/users/aff_whitelists.html +++ b/users/templates/users/aff_whitelists.html @@ -1,5 +1,5 @@ {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/confirm_email.html b/users/templates/users/confirm_email.html index b00d3e88..33beffa0 100644 --- a/users/templates/users/confirm_email.html +++ b/users/templates/users/confirm_email.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/delete.html b/users/templates/users/delete.html index 3e923617..a417bfef 100644 --- a/users/templates/users/delete.html +++ b/users/templates/users/delete.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/edit_listright.html b/users/templates/users/edit_listright.html index d07b31bf..76e3b756 100644 --- a/users/templates/users/edit_listright.html +++ b/users/templates/users/edit_listright.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index.html b/users/templates/users/index.html index 81372635..3c2ce55f 100644 --- a/users/templates/users/index.html +++ b/users/templates/users/index.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_ban.html b/users/templates/users/index_ban.html index 07ad100f..a66309be 100644 --- a/users/templates/users/index_ban.html +++ b/users/templates/users/index_ban.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_clubs.html b/users/templates/users/index_clubs.html index 98591d52..2c607fbf 100644 --- a/users/templates/users/index_clubs.html +++ b/users/templates/users/index_clubs.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_emailaddress.html b/users/templates/users/index_emailaddress.html index e337d2ba..6e1fd6c2 100644 --- a/users/templates/users/index_emailaddress.html +++ b/users/templates/users/index_emailaddress.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_listright.html b/users/templates/users/index_listright.html index 32b18134..7b4d443c 100644 --- a/users/templates/users/index_listright.html +++ b/users/templates/users/index_listright.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_rights.html b/users/templates/users/index_rights.html index d89c454c..e20c5b3a 100644 --- a/users/templates/users/index_rights.html +++ b/users/templates/users/index_rights.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_schools.html b/users/templates/users/index_schools.html index 6e98465b..59e86e8a 100644 --- a/users/templates/users/index_schools.html +++ b/users/templates/users/index_schools.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_serviceusers.html b/users/templates/users/index_serviceusers.html index 0ab7db9b..521325b1 100644 --- a/users/templates/users/index_serviceusers.html +++ b/users/templates/users/index_serviceusers.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_shell.html b/users/templates/users/index_shell.html index e029b107..88c4da14 100644 --- a/users/templates/users/index_shell.html +++ b/users/templates/users/index_shell.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/index_whitelist.html b/users/templates/users/index_whitelist.html index 544e7db8..7cbc4e35 100644 --- a/users/templates/users/index_whitelist.html +++ b/users/templates/users/index_whitelist.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/mass_archive.html b/users/templates/users/mass_archive.html index 5b44f6ec..9585e04c 100644 --- a/users/templates/users/mass_archive.html +++ b/users/templates/users/mass_archive.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/plugin_out.html b/users/templates/users/plugin_out.html index 23e696fe..585c1a0a 100644 --- a/users/templates/users/plugin_out.html +++ b/users/templates/users/plugin_out.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index fddf9af4..dbd5b6e1 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/resend_confirmation_email.html b/users/templates/users/resend_confirmation_email.html index ab226374..27b25344 100644 --- a/users/templates/users/resend_confirmation_email.html +++ b/users/templates/users/resend_confirmation_email.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/sidebar.html b/users/templates/users/sidebar.html index 54d83af3..334a1fdc 100644 --- a/users/templates/users/sidebar.html +++ b/users/templates/users/sidebar.html @@ -1,6 +1,6 @@ {% extends 'base.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/user.html b/users/templates/users/user.html index fe778462..d9302e00 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/templates/users/user_autocapture.html b/users/templates/users/user_autocapture.html index 469e2ec3..c396a288 100644 --- a/users/templates/users/user_autocapture.html +++ b/users/templates/users/user_autocapture.html @@ -1,6 +1,6 @@ {% extends 'users/sidebar.html' %} {% comment %} -Re2o est un logiciel d'administration développé initiallement au rezometz. Il +Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il se veut agnostique au réseau considéré, de manière à être installable en quelques clics. diff --git a/users/tests.py b/users/tests.py index f0ae8d09..4884f971 100644 --- a/users/tests.py +++ b/users/tests.py @@ -1,4 +1,4 @@ -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/urls.py b/users/urls.py index 91f3f3a9..74cdd284 100644 --- a/users/urls.py +++ b/users/urls.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # diff --git a/users/views.py b/users/views.py index c950b197..0b671aba 100644 --- a/users/views.py +++ b/users/views.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au rezometz. Il +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il # se veut agnostique au réseau considéré, de manière à être installable en # quelques clics. # From 0844d7ad7e899bce0ba6ee650f6ec0dd938de61f Mon Sep 17 00:00:00 2001 From: chapeau Date: Mon, 23 Nov 2020 19:58:55 +0100 Subject: [PATCH 386/490] Add functional views in api router --- api/routers.py | 18 ++++++++++++++++++ api/urls.py | 5 +++++ 2 files changed, 23 insertions(+) diff --git a/api/routers.py b/api/routers.py index d81ab402..9703d040 100644 --- a/api/routers.py +++ b/api/routers.py @@ -4,6 +4,7 @@ # quelques clics. # # Copyright © 2018 Mael Kervella +# Copyright © 2020 Corentin Canebier # # 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 @@ -41,6 +42,7 @@ class AllViewsRouter(DefaultRouter): def __init__(self, *args, **kwargs): self.view_registry = [] + self.functional_view_registry = [] super(AllViewsRouter, self).__init__(*args, **kwargs) def register_viewset(self, *args, **kwargs): @@ -64,6 +66,19 @@ class AllViewsRouter(DefaultRouter): name = self.get_default_name(pattern) self.view_registry.append((pattern, view, name)) + def register_functional_view(self, pattern, view, name=None): + """Register a functional view in the router. + + Args: + pattern: The URL pattern to use for this view. + view: The functional view to register. + name: An optional name for the route generated. Defaults is + based on the pattern last section (delimited by '/'). + """ + if name is None: + name = self.get_default_name(pattern) + self.functional_view_registry.append((pattern, view, name)) + @staticmethod def get_default_name(pattern): """Returns the name to use for the route if none was specified. @@ -155,4 +170,7 @@ class AllViewsRouter(DefaultRouter): for pattern, view, name in self.view_registry: urls.append(url(pattern, view.as_view(), name=name)) + for pattern, view, name in self.functional_view_registry: + urls.append(url(pattern, view, name=name)) + return urls diff --git a/api/urls.py b/api/urls.py index e64c1eec..5d06dff1 100644 --- a/api/urls.py +++ b/api/urls.py @@ -4,6 +4,7 @@ # quelques clics. # # Copyright © 2018 Maël Kervella +# Copyright © 2020 Corentin Canebier # # 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 @@ -38,12 +39,14 @@ router = AllViewsRouter() urls_viewset = [] urls_view = [] +urls_functional_view = [] for app in settings.INSTALLED_APPS: try: module = import_module(".api.urls", package=app) urls_viewset += getattr(module, "urls_viewset", []) urls_view += getattr(module, "urls_view", []) + urls_functional_view += getattr(module, "urls_functional_view", []) except ImportError: continue @@ -56,6 +59,8 @@ for _url, viewset, name in urls_viewset: for _url, view in urls_view: router.register_view(_url, view) +for _url, view, name in urls_functional_view: + router.register_functional_view(_url, view, name) # TOKEN AUTHENTICATION router.register_view(r"token-auth", views.ObtainExpiringAuthToken) From a76c9176c52a079f6baa556428c93ee0960bc4cb Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 27 Dec 2020 15:47:41 +0100 Subject: [PATCH 387/490] Fix advanced search filters for empty query --- search/engine.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/search/engine.py b/search/engine.py index dc4eede8..ae772514 100644 --- a/search/engine.py +++ b/search/engine.py @@ -564,4 +564,9 @@ def create_queries(query): queries.append(current_query) + # Make sure there is at least one query, even if it's empty + # Otherwise, display filters (for advanced search) won't work + # when the search text field is empty + queries = queries or [Query()] + return queries From a56f3a4b96f79836923a40dcbd4f0c8e0a53507a Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 27 Dec 2020 15:50:16 +0100 Subject: [PATCH 388/490] Improve code formatting of search/engine.py --- search/engine.py | 110 ++++++++++++++++++++++++----------------------- 1 file changed, 57 insertions(+), 53 deletions(-) diff --git a/search/engine.py b/search/engine.py index ae772514..a724cd8c 100644 --- a/search/engine.py +++ b/search/engine.py @@ -42,6 +42,20 @@ from preferences.models import GeneralOption from re2o.base import SortTable, re2o_paginator +# List of fields the search applies to +FILTER_FIELDS = [ + "users", + "clubs", + "machines", + "factures", + "bans", + "whitelists", + "rooms", + "ports", + "switches", +] + + class Query: """Class representing a query. It can contain the user-entered text, the operator for the query, @@ -53,6 +67,7 @@ class Query: subqueries: list of Query objects when the current query is split in several parts. """ + def __init__(self, text="", case_sensitive=False): """Initialise an instance of Query. @@ -98,27 +113,14 @@ class Query: return self.operator.join([q.plaintext for q in self.subqueries]) if self.case_sensitive: - return "\"{}\"".format(self.text) + return '"{}"'.format(self.text) return self.text -def filter_fields(): - """Return the list of fields the search applies to.""" - return ["users", - "clubs", - "machines", - "factures", - "bans", - "whitelists", - "rooms", - "ports", - "switches"] - - def empty_filters(): """Build empty filters used by Django.""" - return {f: Q() for f in filter_fields()} + return {f: Q() for f in FILTER_FIELDS} def is_int(variable): @@ -176,10 +178,9 @@ def finish_results(request, results, col, order): max_result = GeneralOption.get_cached_value("search_display_page") for name, val in results.items(): page_arg = name + "_page" - results[name] = re2o_paginator(request, - val.distinct(), - max_result, - page_arg=page_arg) + results[name] = re2o_paginator( + request, val.distinct(), max_result, page_arg=page_arg + ) results.update({"max_result": max_result}) @@ -206,9 +207,9 @@ def contains_filter(attribute, word, case_sensitive=False): return Q(**{attr: word}) -def search_single_word(word, filters, user, start, end, - user_state, email_state, aff, - case_sensitive=False): +def search_single_word( + word, filters, user, start, end, user_state, email_state, aff, case_sensitive=False +): """Construct the correct filters to match differents fields of some models with the given query according to the given filters. The match fields are either CharField or IntegerField that will be displayed @@ -230,10 +231,7 @@ def search_single_word(word, filters, user, start, end, ) # Users have a name whereas clubs only have a surname - filter_users = ( - filter_clubs - | contains_filter("name", word, case_sensitive) - ) + filter_users = filter_clubs | contains_filter("name", word, case_sensitive) if not User.can_view_all(user)[0]: filter_clubs &= Q(id=user.id) @@ -252,12 +250,15 @@ def search_single_word(word, filters, user, start, end, if "1" in aff: filter_machines = ( contains_filter("name", word, case_sensitive) - | (contains_filter("user__pseudo", word, case_sensitive) - & Q(user__state__in=user_state) - & Q(user__email_state__in=email_state)) + | ( + contains_filter("user__pseudo", word, case_sensitive) + & Q(user__state__in=user_state) + & Q(user__email_state__in=email_state) + ) | contains_filter("interface__domain__name", word, case_sensitive) - | contains_filter("interface__domain__related_domain__name", - word, case_sensitive) + | contains_filter( + "interface__domain__related_domain__name", word, case_sensitive + ) | contains_filter("interface__mac_address", word, case_sensitive) | contains_filter("interface__ipv4__ipv4", word, case_sensitive) ) @@ -339,13 +340,12 @@ def search_single_word(word, filters, user, start, end, # Switch ports if "6" in aff and User.can_view_all(user): filter_ports = ( - contains_filter("machine_interface__domain__name", - word, case_sensitive) - | contains_filter("related__switch__interface__domain__name", - word, case_sensitive) + contains_filter("machine_interface__domain__name", word, case_sensitive) + | contains_filter( + "related__switch__interface__domain__name", word, case_sensitive + ) | contains_filter("custom_profile__name", word, case_sensitive) - | contains_filter("custom_profile__profil_default", - word, case_sensitive) + | contains_filter("custom_profile__profil_default", word, case_sensitive) | contains_filter("details", word, case_sensitive) # Added through annotate | contains_filter("room_full_name", word, case_sensitive) @@ -360,8 +360,7 @@ def search_single_word(word, filters, user, start, end, filter_switches = ( contains_filter("interface__domain__name", word, case_sensitive) | contains_filter("interface__ipv4__ipv4", word, case_sensitive) - | contains_filter("switchbay__building__name", - word, case_sensitive) + | contains_filter("switchbay__building__name", word, case_sensitive) | contains_filter("stack__name", word, case_sensitive) | contains_filter("model__reference", word, case_sensitive) | contains_filter("model__constructor__name", word, case_sensitive) @@ -399,13 +398,11 @@ def apply_filters(filters, user, aff): # Users and clubs if "0" in aff: results["users"] = Adherent.objects.annotate( - room_full_name=Concat("room__building__name", - Value(" "), "room__name"), + room_full_name=Concat("room__building__name", Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["users"]) results["clubs"] = Club.objects.annotate( - room_full_name=Concat("room__building__name", - Value(" "), "room__name"), + room_full_name=Concat("room__building__name", Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["clubs"]) @@ -435,8 +432,7 @@ def apply_filters(filters, user, aff): # Switch ports if "6" in aff and User.can_view_all(user): results["ports"] = Port.objects.annotate( - room_full_name=Concat("room__building__name", - Value(" "), "room__name"), + room_full_name=Concat("room__building__name", Value(" "), "room__name"), room_full_name_stuck=Concat("room__building__name", "room__name"), ).filter(filters["ports"]) @@ -455,24 +451,32 @@ def search_single_query(query, filters, user, start, end, user_state, email_stat newfilters = empty_filters() for q in query.subqueries: # Construct an independent filter for each subquery - subfilters = search_single_query(q, empty_filters(), user, - start, end, user_state, - email_state, aff) + subfilters = search_single_query( + q, empty_filters(), user, start, end, user_state, email_state, aff + ) # Apply the subfilter - for field in filter_fields(): + for field in FILTER_FIELDS: newfilters[field] &= subfilters[field] # Add these filters to the existing ones - for field in filter_fields(): + for field in FILTER_FIELDS: filters[field] |= newfilters[field] return filters # Handle standard queries - return search_single_word(query.text, filters, user, start, end, - user_state, email_state, aff, - query.case_sensitive) + return search_single_word( + query.text, + filters, + user, + start, + end, + user_state, + email_state, + aff, + query.case_sensitive, + ) def create_queries(query): From 3014b9ac2ed2ac97ddc6991ddbcdfa3faeb12118 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 16:37:43 +0100 Subject: [PATCH 389/490] Remove useless MinValueValidator(0) on PositiveIntegerField. --- .../migrations/0051_auto_20201228_1636.py | 45 +++++++++++++++++++ cotisations/models.py | 6 --- 2 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 cotisations/migrations/0051_auto_20201228_1636.py diff --git a/cotisations/migrations/0051_auto_20201228_1636.py b/cotisations/migrations/0051_auto_20201228_1636.py new file mode 100644 index 00000000..031ccf63 --- /dev/null +++ b/cotisations/migrations/0051_auto_20201228_1636.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-28 15:36 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0050_auto_20201102_2342'), + ] + + operations = [ + migrations.AlterField( + model_name='article', + name='duration_connection', + field=models.PositiveIntegerField(verbose_name='duration of the connection (in months)'), + ), + migrations.AlterField( + model_name='article', + name='duration_days_connection', + field=models.PositiveIntegerField(verbose_name='duration of the connection (in days, will be added to duration in months)'), + ), + migrations.AlterField( + model_name='article', + name='duration_days_membership', + field=models.PositiveIntegerField(verbose_name='duration of the membership (in days, will be added to duration in months)'), + ), + migrations.AlterField( + model_name='article', + name='duration_membership', + field=models.PositiveIntegerField(verbose_name='duration of the membership (in months)'), + ), + migrations.AlterField( + model_name='vente', + name='duration_days_connection', + field=models.PositiveIntegerField(default=0, verbose_name='duration of the connection (in days, will be added to duration in months)'), + ), + migrations.AlterField( + model_name='vente', + name='duration_days_membership', + field=models.PositiveIntegerField(default=0, verbose_name='duration of the membership (in days, will be added to duration in months)'), + ), + ] diff --git a/cotisations/models.py b/cotisations/models.py index b3480561..328a2657 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -467,7 +467,6 @@ class Vente(RevMixin, AclMixin, models.Model): ) duration_days_connection = models.PositiveIntegerField( default=0, - validators=[MinValueValidator(0)], verbose_name=_("duration of the connection (in days, will be added to duration in months)"), ) duration_membership = models.PositiveIntegerField( @@ -475,7 +474,6 @@ class Vente(RevMixin, AclMixin, models.Model): ) duration_days_membership = models.PositiveIntegerField( default=0, - validators=[MinValueValidator(0)], verbose_name=_("duration of the membership (in days, will be added to duration in months)"), ) @@ -704,19 +702,15 @@ class Article(RevMixin, AclMixin, models.Model): ) duration_membership = models.PositiveIntegerField( - validators=[MinValueValidator(0)], verbose_name=_("duration of the membership (in months)") ) duration_days_membership = models.PositiveIntegerField( - validators=[MinValueValidator(0)], verbose_name=_("duration of the membership (in days, will be added to duration in months)"), ) duration_connection = models.PositiveIntegerField( - validators=[MinValueValidator(0)], verbose_name=_("duration of the connection (in months)") ) duration_days_connection = models.PositiveIntegerField( - validators=[MinValueValidator(0)], verbose_name=_("duration of the connection (in days, will be added to duration in months)"), ) From 27343b832325b701a424ccefdb910dc95154da57 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 16:38:44 +0100 Subject: [PATCH 390/490] Code formatting. --- .../migrations/0051_auto_20201228_1636.py | 52 ++++++++++------ cotisations/models.py | 59 ++++++++++++------- 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/cotisations/migrations/0051_auto_20201228_1636.py b/cotisations/migrations/0051_auto_20201228_1636.py index 031ccf63..572c9634 100644 --- a/cotisations/migrations/0051_auto_20201228_1636.py +++ b/cotisations/migrations/0051_auto_20201228_1636.py @@ -8,38 +8,52 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('cotisations', '0050_auto_20201102_2342'), + ("cotisations", "0050_auto_20201102_2342"), ] operations = [ migrations.AlterField( - model_name='article', - name='duration_connection', - field=models.PositiveIntegerField(verbose_name='duration of the connection (in months)'), + model_name="article", + name="duration_connection", + field=models.PositiveIntegerField( + verbose_name="duration of the connection (in months)" + ), ), migrations.AlterField( - model_name='article', - name='duration_days_connection', - field=models.PositiveIntegerField(verbose_name='duration of the connection (in days, will be added to duration in months)'), + model_name="article", + name="duration_days_connection", + field=models.PositiveIntegerField( + verbose_name="duration of the connection (in days, will be added to duration in months)" + ), ), migrations.AlterField( - model_name='article', - name='duration_days_membership', - field=models.PositiveIntegerField(verbose_name='duration of the membership (in days, will be added to duration in months)'), + model_name="article", + name="duration_days_membership", + field=models.PositiveIntegerField( + verbose_name="duration of the membership (in days, will be added to duration in months)" + ), ), migrations.AlterField( - model_name='article', - name='duration_membership', - field=models.PositiveIntegerField(verbose_name='duration of the membership (in months)'), + model_name="article", + name="duration_membership", + field=models.PositiveIntegerField( + verbose_name="duration of the membership (in months)" + ), ), migrations.AlterField( - model_name='vente', - name='duration_days_connection', - field=models.PositiveIntegerField(default=0, verbose_name='duration of the connection (in days, will be added to duration in months)'), + model_name="vente", + name="duration_days_connection", + field=models.PositiveIntegerField( + default=0, + verbose_name="duration of the connection (in days, will be added to duration in months)", + ), ), migrations.AlterField( - model_name='vente', - name='duration_days_membership', - field=models.PositiveIntegerField(default=0, verbose_name='duration of the membership (in days, will be added to duration in months)'), + model_name="vente", + name="duration_days_membership", + field=models.PositiveIntegerField( + default=0, + verbose_name="duration of the membership (in days, will be added to duration in months)", + ), ), ] diff --git a/cotisations/models.py b/cotisations/models.py index 328a2657..5dc18e4f 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -98,7 +98,7 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): def name_detailed(self): """ - Return: + Return: - a list of strings with the name of all article in the invoice and their quantity. """ @@ -248,8 +248,8 @@ class Facture(BaseInvoice): @staticmethod def can_change_control(user_request, *_args, **_kwargs): - """ Returns True if the user can change the 'controlled' status of - this invoice """ + """Returns True if the user can change the 'controlled' status of + this invoice""" can = user_request.has_perm("cotisations.change_facture_control") return ( can, @@ -293,8 +293,7 @@ class Facture(BaseInvoice): """Returns every subscription associated with this invoice.""" return Cotisation.objects.filter( vente__in=self.vente_set.filter( - ~(Q(duration_membership=0)) |\ - ~(Q(duration_days_membership=0)) + ~(Q(duration_membership=0)) | ~(Q(duration_days_membership=0)) ) ) @@ -454,7 +453,7 @@ class Vente(RevMixin, AclMixin, models.Model): number = models.IntegerField( validators=[MinValueValidator(1)], verbose_name=_("amount") ) - # TODO : change this field for a ForeinKey to Article + # TODO : change this field for a ForeinKey to Article # Note: With a foreign key, modifing an Article modifis the Purchase, wich is bad. # To use a foreign key, you need to make Article read only name = models.CharField(max_length=255, verbose_name=_("article")) @@ -467,14 +466,18 @@ class Vente(RevMixin, AclMixin, models.Model): ) duration_days_connection = models.PositiveIntegerField( default=0, - verbose_name=_("duration of the connection (in days, will be added to duration in months)"), + verbose_name=_( + "duration of the connection (in days, will be added to duration in months)" + ), ) duration_membership = models.PositiveIntegerField( default=0, verbose_name=_("duration of the membership (in months)") ) duration_days_membership = models.PositiveIntegerField( default=0, - verbose_name=_("duration of the membership (in days, will be added to duration in months)"), + verbose_name=_( + "duration of the membership (in days, will be added to duration in months)" + ), ) class Meta: @@ -511,7 +514,7 @@ class Vente(RevMixin, AclMixin, models.Model): def create_cotis(self, date_start_con=False, date_start_memb=False): """ - Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date) + Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date) and without saving it. You should use Facture.reorder_purchases to set the right dates. """ try: @@ -629,12 +632,14 @@ class Vente(RevMixin, AclMixin, models.Model): return str(self.name) + " " + str(self.facture) def test_membership_or_connection(self): - """ Test if the purchase include membership or connecton - """ - return self.duration_membership or \ - self.duration_days_membership or \ - self.duration_connection or \ - self.duration_days_connection + """Test if the purchase include membership or connecton""" + return ( + self.duration_membership + or self.duration_days_membership + or self.duration_connection + or self.duration_days_connection + ) + # TODO : change vente to purchase @receiver(post_save, sender=Vente) @@ -705,13 +710,17 @@ class Article(RevMixin, AclMixin, models.Model): verbose_name=_("duration of the membership (in months)") ) duration_days_membership = models.PositiveIntegerField( - verbose_name=_("duration of the membership (in days, will be added to duration in months)"), + verbose_name=_( + "duration of the membership (in days, will be added to duration in months)" + ), ) duration_connection = models.PositiveIntegerField( verbose_name=_("duration of the connection (in months)") ) duration_days_connection = models.PositiveIntegerField( - verbose_name=_("duration of the connection (in days, will be added to duration in months)"), + verbose_name=_( + "duration of the connection (in days, will be added to duration in months)" + ), ) need_membership = models.BooleanField( @@ -787,8 +796,8 @@ class Article(RevMixin, AclMixin, models.Model): if target_user is not None and not target_user.is_adherent(): objects_pool = objects_pool.filter( Q(duration_membership__gt=0) - |Q(duration_days_membership__gt=0) - |Q(need_membership=False) + | Q(duration_days_membership__gt=0) + | Q(need_membership=False) ) if user.has_perm("cotisations.buy_every_article"): return objects_pool @@ -878,7 +887,9 @@ class Paiement(RevMixin, AclMixin, models.Model): # In case a cotisation was bought, inform the user, the # cotisation time has been extended too - if any(sell.test_membership_or_connection() for sell in invoice.vente_set.all()): + if any( + sell.test_membership_or_connection() for sell in invoice.vente_set.all() + ): messages.success( request, _( @@ -950,9 +961,13 @@ class Cotisation(RevMixin, AclMixin, models.Model): vente = models.OneToOneField( "Vente", on_delete=models.CASCADE, null=True, verbose_name=_("purchase") ) - date_start_con = models.DateTimeField(verbose_name=_("start date for the connection")) + date_start_con = models.DateTimeField( + verbose_name=_("start date for the connection") + ) date_end_con = models.DateTimeField(verbose_name=_("end date for the connection")) - date_start_memb = models.DateTimeField(verbose_name=_("start date for the membership")) + date_start_memb = models.DateTimeField( + verbose_name=_("start date for the membership") + ) date_end_memb = models.DateTimeField(verbose_name=_("end date for the membership")) class Meta: From bc644fd32e464869c2cbde390d33dd1c19cfb1b0 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 20:50:00 +0100 Subject: [PATCH 391/490] Fix club edit and add some documentation on that error. --- re2o/acl.py | 10 ++++++++-- users/models.py | 10 +++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/re2o/acl.py b/re2o/acl.py index 600b839f..e73ffdfa 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -64,11 +64,17 @@ def acl_base_decorator(method_name, *targets, on_instance=True): """Base decorator for acl. It checks if the `request.user` has the permission by calling model.method_name. If the flag on_instance is True, tries to get an instance of the model by calling - `model.get_instance(*args, **kwargs)` and runs `instance.mehod_name` + `model.get_instance(obj_id, *args, **kwargs)` and runs `instance.mehod_name` rather than model.method_name. It is not intended to be used as is. It is a base for others ACL - decorators. + decorators. Beware, if you redefine the `get_instance` method for your + model, give it a signature such as + `def get_instance(cls, object_id, *_args, **_kwargs)`, because you will + likely have an url with a named parameter "userid" if *e.g.* your model + is an user. Otherwise, if the parameter name in `get_instance` was also + `userid`, then `get_instance` would end up having two identical parameter + passed on, and this would result in a `TypeError` exception. Args: method_name: The name of the method which is to to be used for ACL. diff --git a/users/models.py b/users/models.py index 67560472..14215fdf 100755 --- a/users/models.py +++ b/users/models.py @@ -2027,10 +2027,10 @@ class Adherent(User): self.gpg_fingerprint = gpg_fingerprint @classmethod - def get_instance(cls, adherentid, *_args, **_kwargs): + def get_instance(cls, object_id, *_args, **_kwargs): """Try to find an instance of `Adherent` with the given id. - :param adherentid: The id of the adherent we are looking for. + :param object_id: The id of the adherent we are looking for. :return: An adherent. """ @@ -2154,13 +2154,13 @@ class Club(User): ) @classmethod - def get_instance(cls, clubid, *_args, **_kwargs): + def get_instance(cls, object_id, *_args, **_kwargs): """Try to find an instance of `Club` with the given id. - :param clubid: The id of the adherent we are looking for. + :param object_id: The id of the adherent we are looking for. :return: A club. """ - return cls.objects.get(pk=clubid) + return cls.objects.get(pk=object_id) @receiver(post_save, sender=Adherent) From 78b473391b22ef30583fb247b6a4bd33be95c708 Mon Sep 17 00:00:00 2001 From: chapeau Date: Sun, 29 Nov 2020 17:17:42 +0100 Subject: [PATCH 392/490] =?UTF-8?q?modification=20des=20acl=20pour=20g?= =?UTF-8?q?=C3=A9rer=20les=20vues=20de=20l'api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- re2o/acl.py | 82 ++++++++++++++++++++++++++++++++++++++------------ re2o/mixins.py | 24 ++++++++++++++- 2 files changed, 86 insertions(+), 20 deletions(-) diff --git a/re2o/acl.py b/re2o/acl.py index e73ffdfa..0a5dd1ee 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -35,6 +35,7 @@ from django.contrib import messages from django.shortcuts import redirect from django.urls import reverse from django.utils.translation import ugettext as _ +from rest_framework.response import Response from re2o.utils import get_group_having_permission @@ -43,11 +44,13 @@ def acl_error_message(msg, permissions): """Create an error message for msg and permissions.""" if permissions is None: return msg - groups = ", ".join([g.name for g in get_group_having_permission(*permissions)]) + groups = ", ".join( + [g.name for g in get_group_having_permission(*permissions)]) message = msg or _("You don't have the right to edit this option.") if groups: return ( - message + _("You need to be a member of one of these groups: %s.") % groups + message + + _("You need to be a member of one of these groups: %s.") % groups ) else: return message + _("No group has the %s permission(s)!") % " or ".join( @@ -60,7 +63,7 @@ def acl_error_message(msg, permissions): # This is the function of main interest of this file. Almost all the decorators # use it, and it is a fairly complicated piece of code. Let me guide you through # this ! 🌈😸 -def acl_base_decorator(method_name, *targets, on_instance=True): +def acl_base_decorator(method_name, *targets, on_instance=True, api=False): """Base decorator for acl. It checks if the `request.user` has the permission by calling model.method_name. If the flag on_instance is True, tries to get an instance of the model by calling @@ -121,6 +124,9 @@ on_instance=False) method `get_instance` of the model, with the arguments originally passed to the view. + api: when set to True, errors will no longer trigger redirection and + messages but will send a 403 with errors in JSON + Returns: The user is either redirected to their own page with an explanation message if at least one access is not granted, or to the view. In order @@ -192,7 +198,8 @@ ModelC) # and store it to pass it to the view. if on_instance: try: - target = target.get_instance(target_id, *args, **kwargs) + target = target.get_instance( + target_id, *args, **kwargs) instances.append(target) except target.DoesNotExist: # A non existing instance is a valid reason to deny @@ -238,28 +245,37 @@ ModelC) # Store the messages at the right place. for can, msg, permissions in process_target(target, fields, target_id): if not can: - error_messages.append(acl_error_message(msg, permissions)) + error_messages.append( + acl_error_message(msg, permissions)) elif msg: - warning_messages.append(acl_error_message(msg, permissions)) + warning_messages.append( + acl_error_message(msg, permissions)) # Display the warning messages - if warning_messages: - for msg in warning_messages: - messages.warning(request, msg) + if not api: + if warning_messages: + for msg in warning_messages: + messages.warning(request, msg) # If there is any error message, then the request must be denied. if error_messages: # We display the message - for msg in error_messages: - messages.error( - request, - msg or _("You don't have the right to access this menu."), - ) + if not api: + for msg in error_messages: + messages.error( + request, + msg or _( + "You don't have the right to access this menu."), + ) # And redirect the user to the right place. if request.user.id is not None: - return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) - ) + if not api: + return redirect( + reverse("users:profil", kwargs={ + "userid": str(request.user.id)}) + ) + else: + return Response(data={"errors": error_messages, "warning": warning_messages}, status=403) else: return redirect(reverse("index")) return view(request, *chain(instances, args), **kwargs) @@ -328,7 +344,8 @@ def can_delete_set(model): request, _("You don't have the right to access this menu.") ) return redirect( - reverse("users:profil", kwargs={"userid": str(request.user.id)}) + reverse("users:profil", kwargs={ + "userid": str(request.user.id)}) ) return view(request, instances, *args, **kwargs) @@ -375,9 +392,36 @@ def can_edit_history(view): """ if request.user.has_perm("admin.change_logentry"): return view(request, *args, **kwargs) - messages.error(request, _("You don't have the right to edit the history.")) + messages.error(request, _( + "You don't have the right to edit the history.")) return redirect( reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) return wrapper + + +def can_view_all_api(*models): + """Decorator to check if an user can see an api page + Only used on functionnal api views (class-based api views ACL are checked + in api/permissions.py) + """ + return acl_base_decorator("can_view_all", *models, on_instance=False, api=True) + + +def can_edit_all_api(*models): + """Decorator to check if an user can edit via the api + We do not always know which instances will be edited, so we may need to know + if the user can edit any instance. + Only used on functionnal api views (class-based api views ACL are checked + in api/permissions.py) + """ + return acl_base_decorator("can_edit_all", *models, on_instance=False, api=True) + + +def can_create_api(*models): + """Decorator to check if an user can create the given models. via the api + Only used on functionnal api views (class-based api views ACL are checked + in api/permissions.py) + """ + return acl_base_decorator("can_create", *models, on_instance=False, api=True) diff --git a/re2o/mixins.py b/re2o/mixins.py index 44cd517e..fbe715f0 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -61,7 +61,8 @@ class FormRevMixin(object): ) elif self.changed_data: reversion.set_comment( - "Field(s) edited: %s" % ", ".join(field for field in self.changed_data) + "Field(s) edited: %s" % ", ".join( + field for field in self.changed_data) ) return super(FormRevMixin, self).save(*args, **kwargs) @@ -187,6 +188,27 @@ class AclMixin(object): (permission,), ) + @classmethod + def can_edit_all(cls, user_request, *_args, **_kwargs): + """Check if a user can edit all instances of an object + + Parameters: + user_request: User calling for this action + + Returns: + Boolean: True if user_request has the right access to do it, else + false with reason for reject authorization + """ + permission = cls.get_modulename() + ".change_" + cls.get_classname() + can = user_request.has_perm(permission) + return ( + can, + _("You don't have the right to edit every %s object.") % cls.get_classname() + if not can + else None, + (permission,), + ) + def can_view(self, user_request, *_args, **_kwargs): """Check if a user can view an instance of an object From a023598e0dbf3e1a24da3370a3ab91645feb185f Mon Sep 17 00:00:00 2001 From: chapeau Date: Sun, 29 Nov 2020 17:21:28 +0100 Subject: [PATCH 393/490] patch --- preferences/views.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/preferences/views.py b/preferences/views.py index 077fd105..5b5f6b03 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -96,7 +96,8 @@ def edit_options_template_function(request, section, forms, models): return redirect(reverse("preferences:display-options")) options_instance, _created = model.objects.get_or_create() - _is_allowed_to_edit, msg, permissions = options_instance.can_edit(request.user) + _is_allowed_to_edit, msg, permissions = options_instance.can_edit( + request.user) if not _is_allowed_to_edit: messages.error(request, acl_error_message(msg, permissions)) return redirect(reverse("index")) @@ -150,7 +151,7 @@ def display_options(request): optionnal_templates_list = [ app.preferences.views.aff_preferences(request) for app in optionnal_apps - if hasattr(app.preferences.views, "aff_preferences") + if hasattr(app, "preferences") and hasattr(app.preferences.views, "aff_preferences") ] return form( @@ -301,7 +302,8 @@ def add_radiuskey(request): @can_edit(RadiusKey) def edit_radiuskey(request, radiuskey_instance, **_kwargs): """View used to edit RADIUS keys.""" - radiuskey = RadiusKeyForm(request.POST or None, instance=radiuskey_instance) + radiuskey = RadiusKeyForm(request.POST or None, + instance=radiuskey_instance) if radiuskey.is_valid(): radiuskey.save() messages.success(request, _("The RADIUS key was edited.")) @@ -344,10 +346,11 @@ def add_switchmanagementcred(request): switchmanagementcred = SwitchManagementCredForm(request.POST or None) if switchmanagementcred.is_valid(): switchmanagementcred.save() - messages.success(request, _("The switch management credentials were added.")) + messages.success(request, _( + "The switch management credentials were added.")) return redirect(reverse("preferences:display-options")) return form( - {"preferenceform": switchmanagementcred, "action_name": _("Add"),}, + {"preferenceform": switchmanagementcred, "action_name": _("Add"), }, "preferences/preferences.html", request, ) @@ -361,7 +364,8 @@ def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs) ) if switchmanagementcred.is_valid(): switchmanagementcred.save() - messages.success(request, _("The switch management credentials were edited.")) + messages.success(request, _( + "The switch management credentials were edited.")) return redirect(reverse("preferences:display-options")) return form( {"preferenceform": switchmanagementcred, "action_name": _("Edit")}, @@ -410,7 +414,7 @@ def add_mailcontact(request): messages.success(request, _("The contact email address was created.")) return redirect(reverse("preferences:display-options")) return form( - {"preferenceform": mailcontact, "action_name": _("Add"),}, + {"preferenceform": mailcontact, "action_name": _("Add"), }, "preferences/preferences.html", request, ) @@ -438,12 +442,14 @@ def edit_mailcontact(request, mailcontact_instance, **_kwargs): @can_delete_set(MailContact) def del_mailcontact(request, instances): """View used to delete one or several contact email addresses.""" - mailcontacts = DelMailContactForm(request.POST or None, instances=instances) + mailcontacts = DelMailContactForm( + request.POST or None, instances=instances) if mailcontacts.is_valid(): mailcontacts_dels = mailcontacts.cleaned_data["mailcontacts"] for mailcontacts_del in mailcontacts_dels: mailcontacts_del.delete() - messages.success(request, _("The contact email adress was deleted.")) + messages.success(request, _( + "The contact email adress was deleted.")) return redirect(reverse("preferences:display-options")) return form( {"preferenceform": mailcontacts, "action_name": _("Delete")}, From 7e60c6ed416f8787d1037afde5d98f13a91b6d30 Mon Sep 17 00:00:00 2001 From: chapeau Date: Sun, 29 Nov 2020 18:19:46 +0100 Subject: [PATCH 394/490] lets be sure that api permissions wont trigger on functional views --- api/permissions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/api/permissions.py b/api/permissions.py index 1983bdc8..3ee61f33 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -239,6 +239,9 @@ class AutodetectACLPermission(permissions.BasePermission): if getattr(view, "_ignore_model_permissions", False): return True + if not getattr(view, "queryset", getattr(view, "get_queryset", None)): + return True + if not request.user or not request.user.is_authenticated: return False @@ -273,7 +276,8 @@ class AutodetectACLPermission(permissions.BasePermission): # they have read permissions to see 403, or not, and simply see # a 404 response. - SAFE_METHODS = ("GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE") + SAFE_METHODS = ("GET", "OPTIONS", "HEAD", + "POST", "PUT", "PATCH", "DELETE") if request.method in SAFE_METHODS: # Read permissions already checked and failed, no need From 5e24867ccdddc967abd7d762fc049a4ef72ca124 Mon Sep 17 00:00:00 2001 From: chapeau Date: Sun, 29 Nov 2020 22:17:52 +0100 Subject: [PATCH 395/490] documentation --- api/permissions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/permissions.py b/api/permissions.py index 3ee61f33..ab7453f4 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -239,6 +239,8 @@ class AutodetectACLPermission(permissions.BasePermission): if getattr(view, "_ignore_model_permissions", False): return True + # Bypass permission verifications if it is a functional view + # (permissions are handled by ACL) if not getattr(view, "queryset", getattr(view, "get_queryset", None)): return True From e9a29c87bee1c731e8b949c814afcb8b5bb82534 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 20:59:35 +0100 Subject: [PATCH 396/490] Just blacked everything. --- api/permissions.py | 3 +-- preferences/views.py | 31 +++++++++++++------------- re2o/acl.py | 52 ++++++++++++++++++++------------------------ re2o/mixins.py | 17 +++++++-------- 4 files changed, 48 insertions(+), 55 deletions(-) diff --git a/api/permissions.py b/api/permissions.py index ab7453f4..646682c4 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -278,8 +278,7 @@ class AutodetectACLPermission(permissions.BasePermission): # they have read permissions to see 403, or not, and simply see # a 404 response. - SAFE_METHODS = ("GET", "OPTIONS", "HEAD", - "POST", "PUT", "PATCH", "DELETE") + SAFE_METHODS = ("GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE") if request.method in SAFE_METHODS: # Read permissions already checked and failed, no need diff --git a/preferences/views.py b/preferences/views.py index 5b5f6b03..5eab39f4 100644 --- a/preferences/views.py +++ b/preferences/views.py @@ -96,8 +96,7 @@ def edit_options_template_function(request, section, forms, models): return redirect(reverse("preferences:display-options")) options_instance, _created = model.objects.get_or_create() - _is_allowed_to_edit, msg, permissions = options_instance.can_edit( - request.user) + _is_allowed_to_edit, msg, permissions = options_instance.can_edit(request.user) if not _is_allowed_to_edit: messages.error(request, acl_error_message(msg, permissions)) return redirect(reverse("index")) @@ -151,7 +150,8 @@ def display_options(request): optionnal_templates_list = [ app.preferences.views.aff_preferences(request) for app in optionnal_apps - if hasattr(app, "preferences") and hasattr(app.preferences.views, "aff_preferences") + if hasattr(app, "preferences") + and hasattr(app.preferences.views, "aff_preferences") ] return form( @@ -302,8 +302,7 @@ def add_radiuskey(request): @can_edit(RadiusKey) def edit_radiuskey(request, radiuskey_instance, **_kwargs): """View used to edit RADIUS keys.""" - radiuskey = RadiusKeyForm(request.POST or None, - instance=radiuskey_instance) + radiuskey = RadiusKeyForm(request.POST or None, instance=radiuskey_instance) if radiuskey.is_valid(): radiuskey.save() messages.success(request, _("The RADIUS key was edited.")) @@ -346,11 +345,13 @@ def add_switchmanagementcred(request): switchmanagementcred = SwitchManagementCredForm(request.POST or None) if switchmanagementcred.is_valid(): switchmanagementcred.save() - messages.success(request, _( - "The switch management credentials were added.")) + messages.success(request, _("The switch management credentials were added.")) return redirect(reverse("preferences:display-options")) return form( - {"preferenceform": switchmanagementcred, "action_name": _("Add"), }, + { + "preferenceform": switchmanagementcred, + "action_name": _("Add"), + }, "preferences/preferences.html", request, ) @@ -364,8 +365,7 @@ def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs) ) if switchmanagementcred.is_valid(): switchmanagementcred.save() - messages.success(request, _( - "The switch management credentials were edited.")) + messages.success(request, _("The switch management credentials were edited.")) return redirect(reverse("preferences:display-options")) return form( {"preferenceform": switchmanagementcred, "action_name": _("Edit")}, @@ -414,7 +414,10 @@ def add_mailcontact(request): messages.success(request, _("The contact email address was created.")) return redirect(reverse("preferences:display-options")) return form( - {"preferenceform": mailcontact, "action_name": _("Add"), }, + { + "preferenceform": mailcontact, + "action_name": _("Add"), + }, "preferences/preferences.html", request, ) @@ -442,14 +445,12 @@ def edit_mailcontact(request, mailcontact_instance, **_kwargs): @can_delete_set(MailContact) def del_mailcontact(request, instances): """View used to delete one or several contact email addresses.""" - mailcontacts = DelMailContactForm( - request.POST or None, instances=instances) + mailcontacts = DelMailContactForm(request.POST or None, instances=instances) if mailcontacts.is_valid(): mailcontacts_dels = mailcontacts.cleaned_data["mailcontacts"] for mailcontacts_del in mailcontacts_dels: mailcontacts_del.delete() - messages.success(request, _( - "The contact email adress was deleted.")) + messages.success(request, _("The contact email adress was deleted.")) return redirect(reverse("preferences:display-options")) return form( {"preferenceform": mailcontacts, "action_name": _("Delete")}, diff --git a/re2o/acl.py b/re2o/acl.py index 0a5dd1ee..f74427ea 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -44,13 +44,11 @@ def acl_error_message(msg, permissions): """Create an error message for msg and permissions.""" if permissions is None: return msg - groups = ", ".join( - [g.name for g in get_group_having_permission(*permissions)]) + groups = ", ".join([g.name for g in get_group_having_permission(*permissions)]) message = msg or _("You don't have the right to edit this option.") if groups: return ( - message + - _("You need to be a member of one of these groups: %s.") % groups + message + _("You need to be a member of one of these groups: %s.") % groups ) else: return message + _("No group has the %s permission(s)!") % " or ".join( @@ -181,8 +179,7 @@ ModelC) # `wrapper` inside the `decorator` function, you need to read some #  documentation on decorators ! def decorator(view): - """The decorator to use on a specific view - """ + """The decorator to use on a specific view""" def wrapper(request, *args, **kwargs): """The wrapper used for a specific request""" @@ -198,8 +195,7 @@ ModelC) # and store it to pass it to the view. if on_instance: try: - target = target.get_instance( - target_id, *args, **kwargs) + target = target.get_instance(target_id, *args, **kwargs) instances.append(target) except target.DoesNotExist: # A non existing instance is a valid reason to deny @@ -245,11 +241,9 @@ ModelC) # Store the messages at the right place. for can, msg, permissions in process_target(target, fields, target_id): if not can: - error_messages.append( - acl_error_message(msg, permissions)) + error_messages.append(acl_error_message(msg, permissions)) elif msg: - warning_messages.append( - acl_error_message(msg, permissions)) + warning_messages.append(acl_error_message(msg, permissions)) # Display the warning messages if not api: @@ -264,18 +258,24 @@ ModelC) for msg in error_messages: messages.error( request, - msg or _( - "You don't have the right to access this menu."), + msg or _("You don't have the right to access this menu."), ) # And redirect the user to the right place. if request.user.id is not None: if not api: return redirect( - reverse("users:profil", kwargs={ - "userid": str(request.user.id)}) + reverse( + "users:profil", kwargs={"userid": str(request.user.id)} + ) ) else: - return Response(data={"errors": error_messages, "warning": warning_messages}, status=403) + return Response( + data={ + "errors": error_messages, + "warning": warning_messages, + }, + status=403, + ) else: return redirect(reverse("index")) return view(request, *chain(instances, args), **kwargs) @@ -326,12 +326,10 @@ def can_delete_set(model): If none of them, return an error""" def decorator(view): - """The decorator to use on a specific view - """ + """The decorator to use on a specific view""" def wrapper(request, *args, **kwargs): - """The wrapper used for a specific request - """ + """The wrapper used for a specific request""" all_objects = model.objects.all() instances_id = [] for instance in all_objects: @@ -344,8 +342,7 @@ def can_delete_set(model): request, _("You don't have the right to access this menu.") ) return redirect( - reverse("users:profil", kwargs={ - "userid": str(request.user.id)}) + reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) return view(request, instances, *args, **kwargs) @@ -373,8 +370,7 @@ def can_view_all(*targets): def can_view_app(*apps_name): - """Decorator to check if an user can view the applications. - """ + """Decorator to check if an user can view the applications.""" for app_name in apps_name: assert app_name in sys.modules.keys() return acl_base_decorator( @@ -388,12 +384,10 @@ def can_edit_history(view): """Decorator to check if an user can edit history.""" def wrapper(request, *args, **kwargs): - """The wrapper used for a specific request - """ + """The wrapper used for a specific request""" if request.user.has_perm("admin.change_logentry"): return view(request, *args, **kwargs) - messages.error(request, _( - "You don't have the right to edit the history.")) + messages.error(request, _("You don't have the right to edit the history.")) return redirect( reverse("users:profil", kwargs={"userid": str(request.user.id)}) ) diff --git a/re2o/mixins.py b/re2o/mixins.py index fbe715f0..26034e07 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -29,9 +29,9 @@ from django.utils.translation import ugettext as _ class RevMixin(object): - """ A mixin to subclass the save and delete function of a model + """A mixin to subclass the save and delete function of a model to enforce the versioning of the object before those actions - really happen """ + really happen""" def save(self, *args, **kwargs): """ Creates a version of this object and save it to database """ @@ -49,8 +49,8 @@ class RevMixin(object): class FormRevMixin(object): - """ A mixin to subclass the save function of a form - to enforce the versionning of the object before it is really edited """ + """A mixin to subclass the save function of a form + to enforce the versionning of the object before it is really edited""" def save(self, *args, **kwargs): """ Create a version of this object and save it to database """ @@ -61,8 +61,7 @@ class FormRevMixin(object): ) elif self.changed_data: reversion.set_comment( - "Field(s) edited: %s" % ", ".join( - field for field in self.changed_data) + "Field(s) edited: %s" % ", ".join(field for field in self.changed_data) ) return super(FormRevMixin, self).save(*args, **kwargs) @@ -130,7 +129,7 @@ class AclMixin(object): Parameters: user_request: User calling for this action - self: Instance to edit + self: Instance to edit Returns: Boolean: True if user_request has the right access to do it, else @@ -151,7 +150,7 @@ class AclMixin(object): Parameters: user_request: User calling for this action - self: Instance to delete + self: Instance to delete Returns: Boolean: True if user_request has the right access to do it, else @@ -214,7 +213,7 @@ class AclMixin(object): Parameters: user_request: User calling for this action - self: Instance to view + self: Instance to view Returns: Boolean: True if user_request has the right access to do it, else From 681d7f10b2a62629001070f3ea2f6ff156157291 Mon Sep 17 00:00:00 2001 From: Yoann Pietri Date: Sun, 22 Nov 2020 09:51:25 +0100 Subject: [PATCH 397/490] Fix display theme on profile --- users/templates/users/profil.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/users/templates/users/profil.html b/users/templates/users/profil.html index dbd5b6e1..3dfff7cf 100644 --- a/users/templates/users/profil.html +++ b/users/templates/users/profil.html @@ -351,7 +351,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Theme" %}
    -
    {{ request.user.theme_name }}
    +
    {{ users.theme_name }}
    From deccc437d56fc9cb33f6995327c77d1f0114042d Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 18:23:55 +0100 Subject: [PATCH 398/490] Fix side effect when importing multi_op --- multi_op/forms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/multi_op/forms.py b/multi_op/forms.py index 59043287..f4c839de 100644 --- a/multi_op/forms.py +++ b/multi_op/forms.py @@ -36,15 +36,17 @@ from topologie.models import Dormitory from .preferences.models import MultiopOption + class DormitoryForm(FormRevMixin, Form): """Form used to select dormitories.""" dormitory = forms.ModelMultipleChoiceField( - queryset=MultiopOption.get_cached_value("enabled_dorm").all(), label=_("Dormitory"), widget=forms.CheckboxSelectMultiple, required=False, + queryset=Dormitory.objects.none(), ) def __init__(self, *args, **kwargs): super(DormitoryForm, self).__init__(*args, **kwargs) + self.fields["dormitory"].queryset = MultiopOption.get_cached_value("enabled_dorm").all() From 639aa86684e81d729aee9255da5d10dcafa21cd2 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 12:15:37 +0100 Subject: [PATCH 399/490] Add views and urls for autocomplete on user app --- users/urls.py | 7 ++++ users/views_autocomplete.py | 72 +++++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 users/views_autocomplete.py diff --git a/users/urls.py b/users/urls.py index 74cdd284..b7d52ddc 100644 --- a/users/urls.py +++ b/users/urls.py @@ -31,6 +31,7 @@ from __future__ import unicode_literals from django.conf.urls import url from . import views +from . import views_autocomplete urlpatterns = [ url(r"^new_user/$", views.new_user, name="new-user"), @@ -128,4 +129,10 @@ urlpatterns = [ url(r"^index_clubs/$", views.index_clubs, name="index-clubs"), url(r"^initial_register/$", views.initial_register, name="initial-register"), url(r"^edit_theme/(?P[0-9]+)$", views.edit_theme, name="edit-theme"), + ### Autocomplete Views + url(r'^user-autocomplete/$', views_autocomplete.UserAutocomplete.as_view(), name='user-autocomplete',), + url(r'^adherent-autocomplete/$', views_autocomplete.AdherentAutocomplete.as_view(), name='adherent-autocomplete',), + url(r'^club-autocomplete/$', views_autocomplete.ClubAutocomplete.as_view(), name='club-autocomplete',), + url(r'^school-autocomplete/$', views_autocomplete.SchoolAutocomplete.as_view(), name='school-autocomplete',), + url(r'^shell-autocomplete/$', views_autocomplete.ShellAutocomplete.as_view(), name='shell-autocomplete',), ] diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py new file mode 100644 index 00000000..a2ea0c06 --- /dev/null +++ b/users/views_autocomplete.py @@ -0,0 +1,72 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Jean-Romain Garnier +# +# 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 +""" +Django views autocomplete view + +Here are defined the autocomplete class based view. + +""" +from __future__ import unicode_literals + +from .models import ( + User, + Ban, + Whitelist, + School, + ListRight, + Request, + ServiceUser, + Adherent, + Club, + ListShell, + EMailAddress, +) + +from re2o.mixins import AutocompleteViewMixin + +from re2o.acl import ( + can_view_all, +) + +#@can_view_all(School) +class SchoolAutocomplete(AutocompleteViewMixin): + obj_type = School + +#@can_view_all(User) +class UserAutocomplete(AutocompleteViewMixin): + obj_type = User + +#@can_view_all(Adherent) +class AdherentAutocomplete(AutocompleteViewMixin): + obj_type = Adherent + +#@can_view_all(Club) +class ClubAutocomplete(AutocompleteViewMixin): + obj_type = Club + +class ShellAutocomplete(AutocompleteViewMixin): + obj_type = ListShell + query_filter = "shell__icontains" From ec4ddb12e6eec44669ca292a1ec5e6d7eabc479c Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 12:16:08 +0100 Subject: [PATCH 400/490] Remove and replace massive_bootstrap; forms change for autocomplete --- users/forms.py | 30 ++++++++++++++++++++++++++---- users/templates/users/user.html | 6 ++++-- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/users/forms.py b/users/forms.py index 1fc00584..3bf19dcc 100644 --- a/users/forms.py +++ b/users/forms.py @@ -60,7 +60,7 @@ from topologie.models import Port from preferences.models import OptionalUser from re2o.utils import remove_user_room from re2o.base import get_input_formats_help_text -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin from re2o.field_permissions import FieldPermissionFormMixin from preferences.models import GeneralOption @@ -350,6 +350,17 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "telephone", "room", ] + widgets = { + "school": AutocompleteModelMixin( + url="/users/school-autocomplete", + ), + "room": AutocompleteModelMixin( + url="/topologie/room-autocomplete", + ), + "shell": AutocompleteModelMixin( + url="/users/shell-autocomplete", + ) + } force = forms.BooleanField( label=_("Force the move?"), initial=False, required=False @@ -461,7 +472,7 @@ class AdherentCreationForm(AdherentForm): # Checkbox for GTU gtu_check = forms.BooleanField(required=True) - class Meta: + class Meta(AdherentForm.Meta): model = Adherent fields = [ "name", @@ -556,7 +567,7 @@ class AdherentEditForm(AdherentForm): if "shell" in self.fields: self.fields["shell"].empty_label = _("Default shell") - class Meta: + class Meta(AdherentForm.Meta): model = Adherent fields = [ "name", @@ -609,6 +620,17 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "shell", "mailing", ] + widgets = { + "school": AutocompleteModelMixin( + url="/users/school-autocomplete", + ), + "room": AutocompleteModelMixin( + url="/topologie/room-autocomplete", + ), + "shell": AutocompleteModelMixin( + url="/users/shell-autocomplete", + ) + } def clean_telephone(self): """Clean telephone, check if telephone is made mandatory, and @@ -1056,4 +1078,4 @@ class ThemeForm(FormRevMixin, forms.Form): if not themes: themes = ["default.css"] super(ThemeForm, self).__init__(*args, **kwargs) - self.fields['theme'].choices = [(theme, theme) for theme in themes] \ No newline at end of file + self.fields['theme'].choices = [(theme, theme) for theme in themes] diff --git a/users/templates/users/user.html b/users/templates/users/user.html index d9302e00..8b143f5c 100644 --- a/users/templates/users/user.html +++ b/users/templates/users/user.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load static %} {% load i18n %} {% block title %}{% trans "Users" %}{% endblock %} @@ -34,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {% csrf_token %} - {% massive_bootstrap_form userform 'room,school,administrators,members' %} + {% bootstrap_form userform %} {% bootstrap_button action_name button_type="submit" icon='ok' button_class='btn-success' %}
    {% if load_js_file %} @@ -48,5 +47,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,


    + +{{ userform.media }} + {% endblock %} From ec6076d169ab8cdae8c8b6f5b92f6d9a68a4ec1f Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 13:20:46 +0100 Subject: [PATCH 401/490] Add views, forms and urls for autocomplete topologie --- topologie/forms.py | 46 ++++++++++- topologie/urls.py | 8 ++ topologie/views_autocomplete.py | 133 ++++++++++++++++++++++++++++++++ 3 files changed, 184 insertions(+), 3 deletions(-) create mode 100644 topologie/views_autocomplete.py diff --git a/topologie/forms.py b/topologie/forms.py index 4044ca3e..348e834e 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -37,7 +37,7 @@ from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import EditMachineForm, NewMachineForm -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from .models import ( Port, @@ -62,6 +62,23 @@ class PortForm(FormRevMixin, ModelForm): class Meta: model = Port fields = "__all__" + widgets = { + "switch": AutocompleteModelMixin( + url="/topologie/switch-autocomplete", + ), + "room": AutocompleteModelMixin( + url="/topologie/room-autocomplete", + ), + "machine_interface": AutocompleteModelMixin( + url="/machine/machine-autocomplete", + ), + "related": AutocompleteModelMixin( + url="/topologie/port-autocomplete", + ), + "custom_profile": AutocompleteModelMixin( + url="/topologie/portprofile-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -180,6 +197,11 @@ class EditRoomForm(FormRevMixin, ModelForm): class Meta: model = Room fields = "__all__" + widgets = { + "building": AutocompleteModelMixin( + url="/topologie/building-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -196,7 +218,11 @@ class CreatePortsForm(forms.Form): class EditModelSwitchForm(FormRevMixin, ModelForm): """Form used to edit switch models.""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + required=False + ) class Meta: model = ModelSwitch @@ -230,11 +256,20 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm): class EditSwitchBayForm(FormRevMixin, ModelForm): """Form used to edit switch bays.""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False, + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + ) class Meta: model = SwitchBay fields = "__all__" + widgets = { + "building": AutocompleteModelMixin( + url="/topologie/building-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -279,6 +314,11 @@ class EditPortProfileForm(FormRevMixin, ModelForm): class Meta: model = PortProfile fields = "__all__" + widgets = { + "vlan_tagged": AutocompleteMultipleModelMixin( + url="/machine/vlan-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) diff --git a/topologie/urls.py b/topologie/urls.py index 9a0ff1e3..c5ac95eb 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -28,6 +28,7 @@ from __future__ import unicode_literals from django.conf.urls import url from . import views +from . import views_autocomplete urlpatterns = [ url(r"^$", views.index, name="index"), @@ -169,4 +170,11 @@ urlpatterns = [ views.del_module_on, name="del-module-on", ), + ### Autocomplete Views + url(r'^room-autocomplete/$', views_autocomplete.RoomAutocomplete.as_view(), name='room-autocomplete',), + url(r'^building-autocomplete/$', views_autocomplete.BuildingAutocomplete.as_view(), name='building-autocomplete',), + url(r'^dormitory-autocomplete/$', views_autocomplete.DormitoryAutocomplete.as_view(), name='dormitory-autocomplete',), + url(r'^switch-autocomplete/$', views_autocomplete.SwitchAutocomplete.as_view(), name='switch-autocomplete',), + url(r'^port-autocomplete/$', views_autocomplete.PortAutocomplete.as_view(), name='profile-autocomplete',), + url(r'^portprofile-autocomplete/$', views_autocomplete.PortProfileAutocomplete.as_view(), name='portprofile-autocomplete',), ] diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py new file mode 100644 index 00000000..36d03c3e --- /dev/null +++ b/topologie/views_autocomplete.py @@ -0,0 +1,133 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Jean-Romain Garnier +# +# 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 +""" +Django views autocomplete view + +Here are defined the autocomplete class based view. + +""" +from __future__ import unicode_literals + +from django.db.models import Q, Value, CharField +from django.db.models.functions import Concat + +from .models import ( + Room, + Dormitory, + Building, + Switch, + PortProfile, + Port, +) + +from re2o.mixins import AutocompleteViewMixin + +from re2o.acl import ( + can_view_all, +) + + +#@can_view_all(School) +class RoomAutocomplete(AutocompleteViewMixin): + obj_type = Room + + # Override get_queryset to add annotations so search behaves more like users expect it to + def get_queryset(self): + # Suppose we have a dorm named Dorm, a building name B, and rooms from 001 - 999 + # Comments explain what we try to match + qs = self.obj_type.objects.annotate( + full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "B 001" + full_name_stuck=Concat("building__name", "name"), # Match "B001" + dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm 001" + dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm B 001" + dorm_full_colon_name=Concat("building__dormitory__name", Value(" : "), "building__name", Value(" "), "name"), # Match "Dorm : B 001" (see Room's full_name property) + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_stuck__icontains=self.q) + | Q(dorm_name__icontains=self.q) + | Q(dorm_full_name__icontains=self.q) + | Q(dorm_full_colon_name__icontains=self.q) + ) + + return qs + + +#@can_view_all(Dormitory) +class DormitoryAutocomplete(AutocompleteViewMixin): + obj_type = Dormitory + + +#@can_view_all(Building) +class BuildingAutocomplete(AutocompleteViewMixin): + obj_type = Building + + def get_queryset(self): + # We want to be able to filter by dorm so it's easier + qs = self.obj_type.objects.annotate( + full_name=Concat("dormitory__name", Value(" "), "name"), + full_name_colon=Concat("dormitory__name", Value(" : "), "name"), + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_colon__icontains=self.q) + ) + + return qs + +class SwitchAutocomplete(AutocompleteViewMixin): + obj_type = Switch + + +class PortAutocomplete(AutocompleteViewMixin): + obj_type = Port + + def get_queryset(self): + # We want to enter the switch name, not just the port number + # Because we're concatenating a CharField and an Integer, we have to sepcify the output_field + qs = self.obj_type.objects.annotate( + full_name=Concat("switch__name", Value(" "), "port", output_field=CharField()), + full_name_stuck=Concat("switch__name", "port", output_field=CharField()), + full_name_dash=Concat("switch__name", Value(" - "), "port", output_field=CharField()), + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(full_name_stuck__icontains=self.q) + | Q(full_name_dash__icontains=self.q) + ) + + return qs + + + +class PortProfileAutocomplete(AutocompleteViewMixin): + obj_type = PortProfile From 46f3f564b9ab6859668583e9776b2ff94479d900 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 13:22:23 +0100 Subject: [PATCH 402/490] Add autocomplete on topologie app --- topologie/templates/topologie/switch.html | 3 +-- topologie/templates/topologie/topo.html | 6 ++++-- topologie/templates/topologie/topo_more.html | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/topologie/templates/topologie/switch.html b/topologie/templates/topologie/switch.html index 6af65bcd..6191b592 100644 --- a/topologie/templates/topologie/switch.html +++ b/topologie/templates/topologie/switch.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Topology" %}{% endblock %} @@ -41,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% csrf_token %} {% if topoform %}

    {% trans "Specific settings for the switch" %}

    - {% massive_bootstrap_form topoform 'switch_interface' %} + {% bootstrap_form topoform %} {% endif %} {% trans "Confirm" as tr_confirm %} {% bootstrap_button tr_confirm button_type="submit" icon='ok' button_class='btn-success' %} diff --git a/topologie/templates/topologie/topo.html b/topologie/templates/topologie/topo.html index 900a2b59..640f40a0 100644 --- a/topologie/templates/topologie/topo.html +++ b/topologie/templates/topologie/topo.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Topology" %}{% endblock %} @@ -37,11 +36,14 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %}
    {% csrf_token %} - {% massive_bootstrap_form topoform 'room,related,machine_interface,members,vlan_tagged,switch' %} + {% bootstrap_form topoform %} {% bootstrap_button action_name icon='ok' button_class='btn-success' %}



    + +{{ topoform.media }} + {% endblock %} diff --git a/topologie/templates/topologie/topo_more.html b/topologie/templates/topologie/topo_more.html index 0180c72f..b0208568 100644 --- a/topologie/templates/topologie/topo_more.html +++ b/topologie/templates/topologie/topo_more.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Topology" %}{% endblock %} @@ -46,11 +45,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% csrf_token %} {% if topoform %}

    {% blocktrans %}Specific settings for the {{ device }} object{% endblocktrans %}

    - {% massive_bootstrap_form topoform 'ipv4,machine' mbf_param=i_mbf_param%} + {% bootstrap_form topoform %} {% endif %} {% if machineform %}

    {% blocktrans %}General settings for the machine linked to the {{ device }} object{% endblocktrans %}

    - {% massive_bootstrap_form machineform 'user' %} + {% bootstrap_form machineform %} {% endif %} {% if domainform %}

    {% trans "DNS name" %}

    From 131ee346c5697f9c122910063ce15240ae617add Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 16:18:11 +0100 Subject: [PATCH 403/490] Improved filter for user search --- users/views_autocomplete.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index a2ea0c06..94f5a93f 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -31,7 +31,7 @@ Here are defined the autocomplete class based view. """ from __future__ import unicode_literals -from .models import ( +from .models import ( User, Ban, Whitelist, @@ -51,6 +51,9 @@ from re2o.acl import ( can_view_all, ) +from django.db.models import Q, Value, CharField +from django.db.models.functions import Concat + #@can_view_all(School) class SchoolAutocomplete(AutocompleteViewMixin): obj_type = School @@ -58,6 +61,22 @@ class SchoolAutocomplete(AutocompleteViewMixin): #@can_view_all(User) class UserAutocomplete(AutocompleteViewMixin): obj_type = User + # Override get_queryset to add annotations so search behaves more like users expect it to + def get_queryset(self): + # Comments explain what we try to match + qs = self.obj_type.objects.annotate( + full_name=Concat("adherent__name", Value(" "), "surname"), # Match when the user searches "Toto Passoir" + full_name_reverse=Concat("surname", Value(" "), "adherent__name"), # Match when the user searches "Passoir Toto" + ).all() + + if self.q: + qs = qs.filter( + Q(pseudo__icontains=self.q) + | Q(full_name__icontains=self.q) + | Q(full_name_reverse__icontains=self.q) + ) + + return qs #@can_view_all(Adherent) class AdherentAutocomplete(AutocompleteViewMixin): From e4d4250dad8d19b4932709ddb271fba47c4e07a0 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 16:18:58 +0100 Subject: [PATCH 404/490] Vlan Autocomplete for portprofile --- topologie/forms.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/topologie/forms.py b/topologie/forms.py index 348e834e..2687d65a 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -316,7 +316,10 @@ class EditPortProfileForm(FormRevMixin, ModelForm): fields = "__all__" widgets = { "vlan_tagged": AutocompleteMultipleModelMixin( - url="/machine/vlan-autocomplete", + url="/machines/vlan-autocomplete", + ), + "vlan_untagged": AutocompleteModelMixin( + url="/machines/vlan-autocomplete", ), } From d00d7019c5054e9fe49b299807451ddb37f6a5ca Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 16:19:29 +0100 Subject: [PATCH 405/490] Add autocomplete on machine form fields --- machines/forms.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++- machines/urls.py | 9 +++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/machines/forms.py b/machines/forms.py index 03f1ecc7..7141bcfc 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -40,7 +40,7 @@ from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from .models import ( Domain, Machine, @@ -71,6 +71,11 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Machine fields = "__all__" + widgets = { + "user": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -91,6 +96,17 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Interface fields = ["machine", "machine_type", "ipv4", "mac_address", "details"] + widgets = { + "machine": AutocompleteModelMixin( + url="/machines/machine-autocomplete", + ), + "machine_type": AutocompleteModelMixin( + url="/machines/machinetype-autocomplete", + ), + "ipv4": AutocompleteModelMixin( + url="/machines/ipv4-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -139,6 +155,11 @@ class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Domain fields = ["name", "extension", "ttl"] + widgets = { + "extension": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -222,6 +243,14 @@ class IpTypeForm(FormRevMixin, ModelForm): class Meta: model = IpType fields = "__all__" + widgets = { + "vlan": AutocompleteModelMixin( + url="/machines/vlan-autocomplete", + ), + "extension": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -351,6 +380,14 @@ class MxForm(FormRevMixin, ModelForm): class Meta: model = Mx fields = ["zone", "priority", "name", "ttl"] + widgets = { + "zone": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + "name": AutocompleteModelMixin( + url="/machines/domain-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -386,6 +423,14 @@ class NsForm(FormRevMixin, ModelForm): class Meta: model = Ns fields = ["zone", "ns", "ttl"] + widgets = { + "zone": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + "ns": AutocompleteModelMixin( + url="/machines/domain-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -419,6 +464,11 @@ class TxtForm(FormRevMixin, ModelForm): class Meta: model = Txt fields = "__all__" + widgets = { + "zone": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -449,6 +499,11 @@ class DNameForm(FormRevMixin, ModelForm): class Meta: model = DName fields = "__all__" + widgets = { + "zone": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -479,6 +534,14 @@ class SrvForm(FormRevMixin, ModelForm): class Meta: model = Srv fields = "__all__" + widgets = { + "extension": AutocompleteModelMixin( + url="/machines/extension-autocomplete", + ), + "target": AutocompleteModelMixin( + url="/machines/domain-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -509,6 +572,14 @@ class NasForm(FormRevMixin, ModelForm): class Meta: model = Nas fields = "__all__" + widgets = { + "nas_type": AutocompleteModelMixin( + url="/machines/machinetype-autocomplete", + ), + "machine_type": AutocompleteModelMixin( + url="/machines/machinetype-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -539,6 +610,11 @@ class RoleForm(FormRevMixin, ModelForm): class Meta: model = Role fields = "__all__" + widgets = { + "servers": AutocompleteMultipleModelMixin( + url="/machines/interface-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -572,6 +648,11 @@ class ServiceForm(FormRevMixin, ModelForm): class Meta: model = Service fields = "__all__" + widgets = { + "servers": AutocompleteMultipleModelMixin( + url="/machines/interface-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -656,6 +737,11 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm): class Meta: model = Interface fields = ["port_lists"] + widgets = { + "port_lists": AutocompleteMultipleModelMixin( + url="/machines/ouvertureportlist-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) diff --git a/machines/urls.py b/machines/urls.py index a8e1dbea..2967f7fd 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -29,6 +29,7 @@ from __future__ import unicode_literals from django.conf.urls import url from . import views +from . import views_autocomplete urlpatterns = [ url(r"^new_machine/(?P[0-9]+)$", views.new_machine, name="new-machine"), @@ -153,4 +154,12 @@ urlpatterns = [ views.configure_ports, name="port-config", ), + ### Autocomplete Views + url(r'^vlan-autocomplete/$', views_autocomplete.VlanAutocomplete.as_view(), name='vlan-autocomplete',), + url(r'^interface-autocomplete/$', views_autocomplete.InterfaceAutocomplete.as_view(), name='interface-autocomplete',), + url(r'^machine-autocomplete/$', views_autocomplete.MachineAutocomplete.as_view(), name='machine-autocomplete',), + url(r'^machinetype-autocomplete/$', views_autocomplete.MachineTypeAutocomplete.as_view(), name='machinetype-autocomplete',), + url(r'^extension-autocomplete/$', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',), + url(r'^domain-autocomplete/$', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',), + url(r'^ouvertureportlist-autocomplete/$', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',), ] From 0aaceab5498072f2409514a67a532a3fe349cd90 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 16:20:33 +0100 Subject: [PATCH 406/490] Remove massive_boostrap form on machine forms --- machines/templates/machines/machine.html | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/machines/templates/machines/machine.html b/machines/templates/machines/machine.html index f74730b2..2cd97097 100644 --- a/machines/templates/machines/machine.html +++ b/machines/templates/machines/machine.html @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Machines" %}{% endblock %} @@ -33,15 +32,18 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} {% if machineform %} {% bootstrap_form_errors machineform %} + {{ machineform.media }} {% endif %} {% if interfaceform %} {% bootstrap_form_errors interfaceform %} + {{ interfaceform.media }} {% endif %} {% if domainform %} {% bootstrap_form_errors domainform %} {% endif %} {% if iptypeform %} {% bootstrap_form_errors iptypeform %} + {{ iptypeform.media }} {% endif %} {% if machinetypeform %} {% bootstrap_form_errors machinetypeform %} @@ -51,36 +53,45 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if mxform %} {% bootstrap_form_errors mxform %} + {{ mxform.media }} {% endif %} {% if nsform %} {% bootstrap_form_errors nsform %} + {{ nsform.media }} {% endif %} {% if txtform %} {% bootstrap_form_errors txtform %} + {{ txtform.media }} {% endif %} {% if dnameform %} {% bootstrap_form_errors dnameform %} + {{ dnameform.media }} {% endif %} {% if srvform %} {% bootstrap_form_errors srvform %} + {{ srvform.media }} {% endif %} {% if aliasform %} {% bootstrap_form_errors aliasform %} + {{ aliasform.media }} {% endif %} {% if serviceform %} {% bootstrap_form_errors serviceform %} + {{ serviceform.media }} {% endif %} {% if sshfpform %} {% bootstrap_form_errors sshfpform %} {% endif %} {% if roleform %} {% bootstrap_form_errors roleform %} + {{ roleform.media }} {% endif %} {% if vlanform %} {% bootstrap_form_errors vlanform %} {% endif %} {% if nasform %} {% bootstrap_form_errors nasform %} + {{ nasform.media }} {% endif %} {% if ipv6form %} {% bootstrap_form_errors ipv6form %} @@ -90,15 +101,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% csrf_token %} {% if machineform %}

    {% trans "Machine" %}

    - {% massive_bootstrap_form machineform 'user' %} + {% bootstrap_form machineform %} {% endif %} {% if interfaceform %}

    {% trans "Interface" %}

    - {% if i_mbf_param %} - {% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' mbf_param=i_mbf_param %} - {% else %} - {% massive_bootstrap_form interfaceform 'ipv4,machine,port_lists' %} - {% endif %} + {% bootstrap_form interfaceform %} {% endif %} {% if domainform %}

    {% trans "Domain" %}

    @@ -114,7 +121,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if extensionform %}

    {% trans "Extension" %}

    - {% massive_bootstrap_form extensionform 'origin' %} + {% bootstrap_form extensionform %} {% endif %} {% if soaform %}

    {% trans "SOA record" %}

    @@ -122,11 +129,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if mxform %}

    {% trans "MX record" %}

    - {% massive_bootstrap_form mxform 'name' %} + {% bootstrap_form mxform %} {% endif %} {% if nsform %}

    {% trans "NS record" %}

    - {% massive_bootstrap_form nsform 'ns' %} + {% bootstrap_form nsform %} {% endif %} {% if txtform %}

    {% trans "TXT record" %}

    @@ -138,7 +145,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if srvform %}

    {% trans "SRV record" %}

    - {% massive_bootstrap_form srvform 'target' %} + {% bootstrap_form srvform %} {% endif %} {% if sshfpform %}

    {% trans "SSHFP record" %}

    @@ -146,15 +153,15 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if aliasform %}

    {% trans "Alias" %}

    - {% massive_bootstrap_form aliasform 'extension' %} + {% bootstrap_form aliasform %} {% endif %} {% if serviceform %}

    {% trans "Service" %}

    - {% massive_bootstrap_form serviceform 'servers' %} + {% bootstrap_form serviceform %} {% endif %} {% if roleform %}

    Role

    - {% massive_bootstrap_form roleform 'servers' %} + {% bootstrap_form roleform %} {% endif %} {% if vlanform %}

    {% trans "VLAN" %}

    From d0a3dda7e4fd7de0bbec38be3ad16ea939217887 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 16:21:33 +0100 Subject: [PATCH 407/490] Add autocomplete view on machine --- machines/views_autocomplete.py | 90 ++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 machines/views_autocomplete.py diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py new file mode 100644 index 00000000..2d4c8931 --- /dev/null +++ b/machines/views_autocomplete.py @@ -0,0 +1,90 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Jean-Romain Garnier +# +# 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 +""" +Django views autocomplete view + +Here are defined the autocomplete class based view. + +""" +from __future__ import unicode_literals + +from django.db.models import Q, Value, CharField +from django.db.models.functions import Concat + +from .models import ( + Interface, + Machine, + Vlan, + MachineType, + Extension, + Domain, + OuverturePortList +) + +from re2o.mixins import AutocompleteViewMixin + +from re2o.acl import ( + can_view_all, +) + + +class VlanAutocomplete(AutocompleteViewMixin): + obj_type = Vlan + + +class MachineAutocomplete(AutocompleteViewMixin): + obj_type = Machine + + +class MachineTypeAutocomplete(AutocompleteViewMixin): + obj_type = MachineType + + +class ExtensionAutocomplete(AutocompleteViewMixin): + obj_type = Extension + + +class DomainAutocomplete(AutocompleteViewMixin): + obj_type = Domain + + +class OuverturePortListAutocomplete(AutocompleteViewMixin): + obj_type = OuverturePortList + + +class InterfaceAutocomplete(AutocompleteViewMixin): + obj_type = Interface + + def get_queryset(self): + qs = self.obj_type.objects.all() + + if self.q: + qs = qs.filter( + Q(domain__name__icontains=self.q) + | Q(machine__name__icontains=self.q) + ) + + return qs From ed487c3d6772f38464110def4ee325084ff94ce4 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 18:26:39 +0100 Subject: [PATCH 408/490] Add autocomplete on switchs/ap edit/creation forms --- topologie/forms.py | 12 +++++++++-- topologie/templates/topologie/topo_more.html | 2 ++ topologie/urls.py | 1 + topologie/views_autocomplete.py | 21 ++++++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) diff --git a/topologie/forms.py b/topologie/forms.py index 2687d65a..fc7a4106 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -171,7 +171,7 @@ class AddAccessPointForm(NewMachineForm): class EditAccessPointForm(EditMachineForm): """Form used to edit access points.""" - class Meta: + class Meta(EditMachineForm.Meta): model = AccessPoint fields = "__all__" @@ -179,9 +179,17 @@ class EditAccessPointForm(EditMachineForm): class EditSwitchForm(EditMachineForm): """Form used to edit switches.""" - class Meta: + class Meta(EditMachineForm.Meta): model = Switch fields = "__all__" + widgets = { + "switchbay": AutocompleteModelMixin( + url="/topologie/switchbay-autocomplete", + ), + "user": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + } class NewSwitchForm(NewMachineForm): diff --git a/topologie/templates/topologie/topo_more.html b/topologie/templates/topologie/topo_more.html index b0208568..74a5265a 100644 --- a/topologie/templates/topologie/topo_more.html +++ b/topologie/templates/topologie/topo_more.html @@ -31,9 +31,11 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} {% if topoform %} {% bootstrap_form_errors topoform %} + {{ topoform.media }} {% endif %} {% if machineform %} {% bootstrap_form_errors machineform %} +{{ machineform.media }} {% endif %} {% if domainform %} {% bootstrap_form_errors domainform %} diff --git a/topologie/urls.py b/topologie/urls.py index c5ac95eb..05688fd3 100644 --- a/topologie/urls.py +++ b/topologie/urls.py @@ -177,4 +177,5 @@ urlpatterns = [ url(r'^switch-autocomplete/$', views_autocomplete.SwitchAutocomplete.as_view(), name='switch-autocomplete',), url(r'^port-autocomplete/$', views_autocomplete.PortAutocomplete.as_view(), name='profile-autocomplete',), url(r'^portprofile-autocomplete/$', views_autocomplete.PortProfileAutocomplete.as_view(), name='portprofile-autocomplete',), + url(r'^switchbay-autocomplete/$', views_autocomplete.SwitchBayAutocomplete.as_view(), name='switchbay-autocomplete',), ] diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index 36d03c3e..bef31fdb 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -41,6 +41,7 @@ from .models import ( Switch, PortProfile, Port, + SwitchBay, ) from re2o.mixins import AutocompleteViewMixin @@ -128,6 +129,26 @@ class PortAutocomplete(AutocompleteViewMixin): return qs +class SwitchBayAutocomplete(AutocompleteViewMixin): + obj_type = SwitchBay + + def get_queryset(self): + # Comments explain what we try to match + qs = self.obj_type.objects.annotate( + full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "" + dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm Local Sud" + dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm J Local Sud" + ).all() + + if self.q: + qs = qs.filter( + Q(full_name__icontains=self.q) + | Q(dorm_name__icontains=self.q) + | Q(dorm_full_name__icontains=self.q) + ) + + return qs + class PortProfileAutocomplete(AutocompleteViewMixin): obj_type = PortProfile From 2aacec6584eeb7479eea486be994b7ae5aefb517 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 18:27:15 +0100 Subject: [PATCH 409/490] Add autocomplete on machine, interface edit forms --- machines/forms.py | 5 ++++- machines/urls.py | 1 + machines/views_autocomplete.py | 19 ++++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/machines/forms.py b/machines/forms.py index 7141bcfc..0c78be3e 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -104,7 +104,10 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): url="/machines/machinetype-autocomplete", ), "ipv4": AutocompleteModelMixin( - url="/machines/ipv4-autocomplete", + url="/machines/iplist-autocomplete", forward=['machine_type'], + attrs={ + 'data-placeholder': 'Automatic assigment. Type to choose specific ip.', + } ), } diff --git a/machines/urls.py b/machines/urls.py index 2967f7fd..387bdae1 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -162,4 +162,5 @@ urlpatterns = [ url(r'^extension-autocomplete/$', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',), url(r'^domain-autocomplete/$', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',), url(r'^ouvertureportlist-autocomplete/$', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',), + url(r'^iplist-autocomplete/$', views_autocomplete.IpListAutocomplete.as_view(), name='iplist-autocomplete',), ] diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index 2d4c8931..a79fd276 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -41,7 +41,8 @@ from .models import ( MachineType, Extension, Domain, - OuverturePortList + OuverturePortList, + IpList ) from re2o.mixins import AutocompleteViewMixin @@ -88,3 +89,19 @@ class InterfaceAutocomplete(AutocompleteViewMixin): ) return qs + + +class IpListAutocomplete(AutocompleteViewMixin): + obj_type = IpList + + def get_queryset(self): + machine_type = self.forwarded.get('machine_type', None) + qs = self.obj_type.objects.filter(interface__isnull=True) + if machine_type: + qs = qs.filter(ip_type__machinetype__id=machine_type) + if self.q: + qs = qs.filter( + Q(ipv4__startswith=self.q) + ) + + return qs From e663fb63e0461083be653091b4ca5f3d253e40f3 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 18:46:37 +0100 Subject: [PATCH 410/490] Add ouverture ports on autocomplete --- machines/forms.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/machines/forms.py b/machines/forms.py index 0c78be3e..f663f5a9 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -253,6 +253,9 @@ class IpTypeForm(FormRevMixin, ModelForm): "extension": AutocompleteModelMixin( url="/machines/extension-autocomplete", ), + "ouverture_ports": AutocompleteModelMixin( + url="/machines/ouvertureportlist-autocomplete", + ), } def __init__(self, *args, **kwargs): From 4ff555e41f0e2a2f5ef75f5d9e185677206d2a29 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 18:47:20 +0100 Subject: [PATCH 411/490] Remove mbf on views --- machines/views.py | 91 ---------------------------------------------- topologie/views.py | 9 ----- 2 files changed, 100 deletions(-) diff --git a/machines/views.py b/machines/views.py index 8ec85583..04b8a3a8 100644 --- a/machines/views.py +++ b/machines/views.py @@ -124,91 +124,6 @@ from .models import ( ) - -def f_type_id(is_type_tt): - """ The id that will be used in HTML to store the value of the field - type. Depends on the fact that type is generate using typeahead or not - """ - return ( - "id_Interface-machine_type_hidden" - if is_type_tt - else "id_Interface-machine_type" - ) - - -def generate_ipv4_choices(form_obj): - """ Generate the parameter choices for the massive_bootstrap_form tag - """ - f_ipv4 = form_obj.fields["ipv4"] - used_mtype_id = [] - choices = '{"":[{key:"",value:"' + _("Select a machine type first.") + '"}' - mtype_id = -1 - - for ip in f_ipv4.queryset.annotate(mtype_id=F("ip_type__machinetype__id")).order_by( - "mtype_id", "id" - ): - if mtype_id != ip.mtype_id: - mtype_id = ip.mtype_id - used_mtype_id.append(mtype_id) - choices += '],"{t}":[{{key:"",value:"{v}"}},'.format( - t=mtype_id, v=f_ipv4.empty_label or '""' - ) - choices += '{{key:{k},value:"{v}"}},'.format(k=ip.id, v=ip.ipv4) - - for t in form_obj.fields["machine_type"].queryset.exclude(id__in=used_mtype_id): - choices += '], "' + str(t.id) + '": [' - choices += '{key: "", value: "' + str(f_ipv4.empty_label) + '"},' - choices += "]}" - return choices - - -def generate_ipv4_engine(is_type_tt): - """ Generate the parameter engine for the massive_bootstrap_form tag - """ - return ( - "new Bloodhound( {{" - 'datumTokenizer: Bloodhound.tokenizers.obj.whitespace( "value" ),' - "queryTokenizer: Bloodhound.tokenizers.whitespace," - 'local: choices_ipv4[ $( "#{machine_type_id}" ).val() ],' - "identify: function( obj ) {{ return obj.key; }}" - "}} )" - ).format(machine_type_id=f_type_id(is_type_tt)) - - -def generate_ipv4_match_func(is_type_tt): - """ Generate the parameter match_func for the massive_bootstrap_form tag - """ - return ( - "function(q, sync) {{" - 'if (q === "") {{' - 'var first = choices_ipv4[$("#{machine_type_id}").val()].slice(0, 5);' - "first = first.map( function (obj) {{ return obj.key; }} );" - "sync(engine_ipv4.get(first));" - "}} else {{" - "engine_ipv4.search(q, sync);" - "}}" - "}}" - ).format(machine_type_id=f_type_id(is_type_tt)) - - -def generate_ipv4_mbf_param(form_obj, is_type_tt): - """ Generate all the parameters to use with the massive_bootstrap_form - tag """ - i_choices = {"ipv4": generate_ipv4_choices(form_obj)} - i_engine = {"ipv4": generate_ipv4_engine(is_type_tt)} - i_match_func = {"ipv4": generate_ipv4_match_func(is_type_tt)} - i_update_on = {"ipv4": [f_type_id(is_type_tt)]} - i_gen_select = {"ipv4": False} - i_mbf_param = { - "choices": i_choices, - "engine": i_engine, - "match_func": i_match_func, - "update_on": i_update_on, - "gen_select": i_gen_select, - } - return i_mbf_param - - @login_required @can_create(Machine) @can_edit(User) @@ -235,13 +150,11 @@ def new_machine(request, user, **_kwargs): new_domain.save() messages.success(request, _("The machine was created.")) return redirect(reverse("users:profil", kwargs={"userid": str(user.id)})) - i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( { "machineform": machine, "interfaceform": interface, "domainform": domain, - "i_mbf_param": i_mbf_param, "action_name": _("Add"), }, "machines/machine.html", @@ -281,13 +194,11 @@ def edit_interface(request, interface_instance, **_kwargs): kwargs={"userid": str(interface_instance.machine.user.id)}, ) ) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { "machineform": machine_form, "interfaceform": interface_form, "domainform": domain_form, - "i_mbf_param": i_mbf_param, "action_name": _("Edit"), }, "machines/machine.html", @@ -332,12 +243,10 @@ def new_interface(request, machine, **_kwargs): return redirect( reverse("users:profil", kwargs={"userid": str(machine.user.id)}) ) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { "interfaceform": interface_form, "domainform": domain_form, - "i_mbf_param": i_mbf_param, "action_name": _("Add"), }, "machines/machine.html", diff --git a/topologie/views.py b/topologie/views.py index ff4db520..93c21d0e 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -56,7 +56,6 @@ from machines.forms import ( AddInterfaceForm, EditOptionVlanForm, ) -from machines.views import generate_ipv4_mbf_param from machines.models import Interface, Service_link, Vlan from preferences.models import AssoOption, GeneralOption @@ -560,13 +559,11 @@ def new_switch(request): new_domain_obj.save() messages.success(request, _("The switch was created.")) return redirect(reverse("topologie:index")) - i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( { "topoform": interface, "machineform": switch, "domainform": domain, - "i_mbf_param": i_mbf_param, "device": _("switch"), }, "topologie/topo_more.html", @@ -634,14 +631,12 @@ def edit_switch(request, switch, switchid): new_domain_obj.save() messages.success(request, _("The switch was edited.")) return redirect(reverse("topologie:index")) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { "id_switch": switchid, "topoform": interface_form, "machineform": switch_form, "domainform": domain_form, - "i_mbf_param": i_mbf_param, "device": _("switch"), }, "topologie/topo_more.html", @@ -686,13 +681,11 @@ def new_ap(request): new_domain_obj.save() messages.success(request, _("The access point was created.")) return redirect(reverse("topologie:index-ap")) - i_mbf_param = generate_ipv4_mbf_param(interface, False) return form( { "topoform": interface, "machineform": ap, "domainform": domain, - "i_mbf_param": i_mbf_param, "device": _("access point"), }, "topologie/topo_more.html", @@ -737,13 +730,11 @@ def edit_ap(request, ap, **_kwargs): new_domain_obj.save() messages.success(request, _("The access point was edited.")) return redirect(reverse("topologie:index-ap")) - i_mbf_param = generate_ipv4_mbf_param(interface_form, False) return form( { "topoform": interface_form, "machineform": ap_form, "domainform": domain_form, - "i_mbf_param": i_mbf_param, "device": _("access point"), }, "topologie/topo_more.html", From 5f46ce1db5477342eb297c3cd99097829f1427a1 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 18:57:08 +0100 Subject: [PATCH 412/490] Add autocomplete on cotisations --- cotisations/forms.py | 10 +++- .../templates/cotisations/edit_facture.html | 4 +- cotisations/urls.py | 4 +- cotisations/views_autocomplete.py | 50 +++++++++++++++++++ 4 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 cotisations/views_autocomplete.py diff --git a/cotisations/forms.py b/cotisations/forms.py index 0f135963..926db818 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -45,7 +45,7 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from .models import ( Article, Paiement, @@ -79,6 +79,14 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): class Meta: model = Facture fields = "__all__" + widgets = { + "user": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + "banque": AutocompleteModelMixin( + url="/cotisations/banque-autocomplete", + ), + } def clean(self): cleaned_data = super(FactureForm, self).clean() diff --git a/cotisations/templates/cotisations/edit_facture.html b/cotisations/templates/cotisations/edit_facture.html index ca55cb66..b2f58df9 100644 --- a/cotisations/templates/cotisations/edit_facture.html +++ b/cotisations/templates/cotisations/edit_facture.html @@ -25,13 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load staticfiles%} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Creation and editing of invoices" %}{% endblock %} {% block content %} {% bootstrap_form_errors factureform %} +{{ factureform.media }}
    {% csrf_token %} @@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% else %}

    {% trans "Edit invoice" %}

    {% endif %} - {% massive_bootstrap_form factureform 'user' %} + {% bootstrap_form factureform %} {{ venteform.management_form }}

    {% trans "Articles" %}

    diff --git a/cotisations/urls.py b/cotisations/urls.py index 6baf74c7..c78effa6 100644 --- a/cotisations/urls.py +++ b/cotisations/urls.py @@ -27,7 +27,7 @@ from __future__ import unicode_literals from django.conf.urls import url -from . import views +from . import views, views_autocomplete from . import payment_methods urlpatterns = [ @@ -104,4 +104,6 @@ urlpatterns = [ url(r"^index_paiement/$", views.index_paiement, name="index-paiement"), url(r"^control/$", views.control, name="control"), url(r"^$", views.index, name="index"), + ### Autocomplete Views + url(r'^banque-autocomplete/$', views_autocomplete.BanqueAutocomplete.as_view(), name='banque-autocomplete',), ] + payment_methods.urls.urlpatterns diff --git a/cotisations/views_autocomplete.py b/cotisations/views_autocomplete.py new file mode 100644 index 00000000..70afe855 --- /dev/null +++ b/cotisations/views_autocomplete.py @@ -0,0 +1,50 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2017-2020 Gabriel Détraz +# Copyright © 2017-2020 Jean-Romain Garnier +# +# 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. + +# App de gestion des users pour re2o +# Lara Kermarec, Gabriel Détraz, Lemesle Augustin +# Gplv2 +""" +Django views autocomplete view + +Here are defined the autocomplete class based view. + +""" +from __future__ import unicode_literals + +from django.db.models import Q, Value, CharField + +from .models import ( + Banque +) + +from re2o.mixins import AutocompleteViewMixin + +from re2o.acl import ( + can_view_all, +) + + +class BanqueAutocomplete(AutocompleteViewMixin): + obj_type = Banque + + From 82f48cfce01f5bc4809366d99c8fb5a69e3424cb Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 19:45:25 +0100 Subject: [PATCH 413/490] Autocomplete on ticket --- tickets/forms.py | 7 ++++++- tickets/templates/tickets/edit.html | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tickets/forms.py b/tickets/forms.py index e28bfa85..ecde5492 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -28,7 +28,7 @@ from django import forms from django.template.loader import render_to_string from django.forms import ModelForm, Form from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from django.utils.translation import ugettext_lazy as _ from .models import Ticket, CommentTicket @@ -58,6 +58,11 @@ class EditTicketForm(FormRevMixin, ModelForm): class Meta: model = Ticket fields = "__all__" + widgets = { + "user": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + } def __init__(self, *args, **kwargs): super(EditTicketForm, self).__init__(*args, **kwargs) diff --git a/tickets/templates/tickets/edit.html b/tickets/templates/tickets/edit.html index 3c5c1c3f..751ab8f5 100644 --- a/tickets/templates/tickets/edit.html +++ b/tickets/templates/tickets/edit.html @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Ticket" %}{% endblock %} @@ -34,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Ticket opening" %}

    {% bootstrap_form_errors ticketform %} +{{ ticketform.media }} {% csrf_token %} From e6f8b6426aa9fbf2ba748382920154ccb7ac5a33 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 19:46:17 +0100 Subject: [PATCH 414/490] Remove templatetag massive bootstrap form --- re2o/templatetags/massive_bootstrap_form.py | 752 -------------------- 1 file changed, 752 deletions(-) delete mode 100644 re2o/templatetags/massive_bootstrap_form.py diff --git a/re2o/templatetags/massive_bootstrap_form.py b/re2o/templatetags/massive_bootstrap_form.py deleted file mode 100644 index 449f7c24..00000000 --- a/re2o/templatetags/massive_bootstrap_form.py +++ /dev/null @@ -1,752 +0,0 @@ -# -*- mode: python; coding: utf-8 -*- -# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il -# se veut agnostique au réseau considéré, de manière à être installable en -# quelques clics. -# -# Copyright © 2017 Maël Kervella -# -# 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. - -""" Templatetag used to render massive django form selects into bootstrap -forms that can still be manipulating even if there is multiple tens of -thousands of elements in the select. It's made possible using JS libaries -Twitter Typeahead and Splitree's Tokenfield. -See docstring of massive_bootstrap_form for a detailed explaantion on how -to use this templatetag. -""" - -from django import template -from django.utils.safestring import mark_safe -from django.forms import TextInput -from django.forms.widgets import Select -from django.utils.translation import ugettext_lazy as _ -from bootstrap3.utils import render_tag -from bootstrap3.forms import render_field - -register = template.Library() - - -@register.simple_tag -def massive_bootstrap_form(form, mbf_fields, *args, **kwargs): - """ - Render a form where some specific fields are rendered using Twitter - Typeahead and/or splitree's Bootstrap Tokenfield to improve the - performance, the speed and UX when dealing with very large datasets - (select with 50k+ elts for instance). - When the fields specified should normally be rendered as a select with - single selectable option, Twitter Typeahead is used for a better display - and the matching query engine. When dealing with multiple selectable - options, sliptree's Bootstrap Tokenfield in addition with Typeahead. - For convenience, it accepts the same parameters as a standard bootstrap - can accept. - - **Tag name**:: - - massive_bootstrap_form - - **Parameters**: - - form (required) - The form that is to be rendered - - mbf_fields (optional) - A list of field names (comma separated) that should be rendered - with Typeahead/Tokenfield instead of the default bootstrap - renderer. - If not specified, all fields will be rendered as a normal bootstrap - field. - - mbf_param (optional) - A dict of parameters for the massive_bootstrap_form tag. The - possible parameters are the following. - - choices (optional) - A dict of strings representing the choices in JS. The keys of - the dict are the names of the concerned fields. The choices - must be an array of objects. Each of those objects must at - least have the fields 'key' (value to send) and 'value' (value - to display). Other fields can be added as desired. - For a more complex structure you should also consider - reimplementing the engine and the match_func. - If not specified, the key is the id of the object and the value - is its string representation as in a normal bootstrap form. - Example : - 'choices' : { - 'field_A':'[{key:0,value:"choice0",extra:"data0"},{...},...]', - 'field_B':..., - ... - } - - engine (optional) - A dict of strings representating the engine used for matching - queries and possible values with typeahead. The keys of the - dict are the names of the concerned fields. The string is valid - JS code. - If not specified, BloodHound with relevant basic properties is - used. - Example : - 'engine' : {'field_A': 'new Bloodhound()', 'field_B': ..., ...} - - match_func (optional) - A dict of strings representing a valid JS function used in the - dataset to overload the matching engine. The keys of the dict - are the names of the concerned fields. This function is used - the source of the dataset. This function receives 2 parameters, - the query and the synchronize function as specified in - typeahead.js documentation. If needed, the local variables - 'choices_' and 'engine_' contains - respectively the array of all possible values and the engine - to match queries with possible values. - If not specified, the function used display up to the 10 first - elements if the query is empty and else the matching results. - Example : - 'match_func' : { - 'field_A': 'function(q, sync) { engine.search(q, sync); }', - 'field_B': ..., - ... - } - - update_on (optional) - A dict of list of ids that the values depends on. The engine - and the typeahead properties are recalculated and reapplied. - Example : - 'update_on' : { - 'field_A' : [ 'id0', 'id1', ... ] , - 'field_B' : ... , - ... - } - - gen_select (optional) - A dict of boolean telling if the form should either generate - the normal select (set to true) and then use it to generate - the possible choices and then remove it or either (set to - false) generate the choices variable in this tag and do not - send any select. - Sending the select before can be usefull to permit the use - without any JS enabled but it will execute more code locally - for the client so the loading might be slower. - If not specified, this variable is set to true for each field - Example : - 'gen_select' : { - 'field_A': True , - 'field_B': ... , - ... - } - - See boostrap_form_ for other arguments - - **Usage**:: - - {% massive_bootstrap_form - form - [ '[,[,...]]' ] - [ mbf_param = { - [ 'choices': { - [ '': '' - [, '': '' - [, ... ] ] ] - } ] - [, 'engine': { - [ '': '' - [, '': '' - [, ... ] ] ] - } ] - [, 'match_func': { - [ '': '' - [, '': '' - [, ... ] ] ] - } ] - [, 'update_on': { - [ '': '' - [, '': '' - [, ... ] ] ] - } ], - [, 'gen_select': { - [ '': '' - [, '': '' - [, ... ] ] ] - } ] - } ] - [ ] - %} - - **Example**: - - {% massive_bootstrap_form form 'ipv4' choices='[...]' %} - """ - - mbf_form = MBFForm(form, mbf_fields.split(","), *args, **kwargs) - return mbf_form.render() - - -class MBFForm: - """ An object to hold all the information and useful methods needed to - create and render a massive django form into an actual HTML and JS - code able to handle it correctly. - Every field that is not listed is rendered as a normal bootstrap_field. - """ - - def __init__(self, form, mbf_fields, *args, **kwargs): - # The django form object - self.form = form - # The fields on which to use JS - self.fields = mbf_fields - - # Other bootstrap_form arguments to render the fields - self.args = args - self.kwargs = kwargs - - # Fields to exclude form the form rendering - self.exclude = self.kwargs.get("exclude", "").split(",") - - # All the mbf parameters specified byt the user - param = kwargs.pop("mbf_param", {}) - self.choices = param.get("choices", {}) - self.engine = param.get("engine", {}) - self.match_func = param.get("match_func", {}) - self.update_on = param.get("update_on", {}) - self.gen_select = param.get("gen_select", {}) - self.hidden_fields = [h.name for h in self.form.hidden_fields()] - - # HTML code to insert inside a template - self.html = "" - - def render(self): - """ HTML code for the fully rendered form with all the necessary form - """ - for name, field in self.form.fields.items(): - if name not in self.exclude: - - if name in self.fields and name not in self.hidden_fields: - mbf_field = MBFField( - name, - field, - field.get_bound_field(self.form, name), - self.choices.get(name, None), - self.engine.get(name, None), - self.match_func.get(name, None), - self.update_on.get(name, None), - self.gen_select.get(name, True), - *self.args, - **self.kwargs - ) - self.html += mbf_field.render() - - else: - f = field.get_bound_field(self.form, name), self.args, self.kwargs - self.html += render_field( - field.get_bound_field(self.form, name), - *self.args, - **self.kwargs - ) - - return mark_safe(self.html) - - -class MBFField: - """ An object to hold all the information and useful methods needed to - create and render a massive django form field into an actual HTML and JS - code able to handle it correctly. - Twitter Typeahead is used for the display and the matching of queries and - in case of a MultipleSelect, Sliptree's Tokenfield is also used to manage - multiple values. - A div with only non visible elements is created after the div containing - the displayed input. It's used to store the actual data that will be sent - to the server """ - - def __init__( - self, - name_, - field_, - bound_, - choices_, - engine_, - match_func_, - update_on_, - gen_select_, - *args_, - **kwargs_ - ): - - # Verify this field is a Select (or MultipleSelect) (only supported) - if not isinstance(field_.widget, Select): - raise ValueError( - ( - "Field named {f_name} is not a Select and" - "can't be rendered with massive_bootstrap_form." - ).format(f_name=name_) - ) - - # Name of the field - self.name = name_ - # Django field object - self.field = field_ - # Bound Django field associated with field - self.bound = bound_ - - # Id for the main visible input - self.input_id = self.bound.auto_id - # Id for a hidden input used to store the value - self.hidden_id = self.input_id + "_hidden" - # Id for another div containing hidden inputs and script - self.div2_id = self.input_id + "_div" - - # Should the standard select should be generated - self.gen_select = gen_select_ - # Is it select with multiple values possible (use of tokenfield) - self.multiple = self.field.widget.allow_multiple_selected - # JS for the choices variable (user specified or default) - self.choices = choices_ or self.default_choices() - # JS for the engine variable (typeahead) (user specified or default) - self.engine = engine_ or self.default_engine() - # JS for the matching function (typeahead) (user specified or default) - self.match_func = match_func_ or self.default_match_func() - # JS for the datasets variable (typeahead) (user specified or default) - self.datasets = self.default_datasets() - # Ids of other fields to bind a reset/reload with when changed - self.update_on = update_on_ or [] - - # Whole HTML code to insert in the template - self.html = "" - # JS code in the script tag - self.js_script = "" - # Input tag to display instead of select - self.replace_input = None - - # Other bootstrap_form arguments to render the fields - self.args = args_ - self.kwargs = kwargs_ - - def default_choices(self): - """ JS code of the variable choices_ """ - - if self.gen_select: - return ( - "function plop(o) {{" - "var c = [];" - "for( let i=0 ; i """ - return ( - "new Bloodhound({{" - ' datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),' - " queryTokenizer: Bloodhound.tokenizers.whitespace," - " local: choices_{name}," - " identify: function(obj) {{ return obj.key; }}" - "}})" - ).format(name=self.name) - - def default_datasets(self): - """ Default JS script of the datasets to use with typeahead """ - return ( - "{{" - " hint: true," - " highlight: true," - " minLength: 0" - "}}," - "{{" - ' display: "value",' - ' name: "{name}",' - " source: {match_func}" - "}}" - ).format(name=self.name, match_func=self.match_func) - - def default_match_func(self): - """ Default JS code of the matching function to use with typeahed """ - return ( - "function ( q, sync ) {{" - ' if ( q === "" ) {{' - " var first = choices_{name}.slice( 0, 5 ).map(" - " function ( obj ) {{ return obj.key; }}" - " );" - " sync( engine_{name}.get( first ) );" - " }} else {{" - " engine_{name}.search( q, sync );" - " }}" - "}}" - ).format(name=self.name) - - def render(self): - """ HTML code for the fully rendered field """ - self.gen_displayed_div() - self.gen_hidden_div() - return mark_safe(self.html) - - def gen_displayed_div(self): - """ Generate HTML code for the div that contains displayed tags """ - if self.gen_select: - self.html += render_field(self.bound, *self.args, **self.kwargs) - - self.field.widget = TextInput( - attrs={ - "name": "mbf_" + self.name, - "placeholder": getattr(self.field, "empty_label", _("Nothing")), - } - ) - self.replace_input = render_field(self.bound, *self.args, **self.kwargs) - - if not self.gen_select: - self.html += self.replace_input - - def gen_hidden_div(self): - """ Generate HTML code for the div that contains hidden tags """ - self.gen_full_js() - - content = self.js_script - if not self.multiple and not self.gen_select: - content += self.hidden_input() - - self.html += render_tag("div", content=content, attrs={"id": self.div2_id}) - - def hidden_input(self): - """ HTML for the hidden input element """ - return render_tag( - "input", - attrs={ - "id": self.hidden_id, - "name": self.bound.html_name, - "type": "hidden", - "value": self.bound.value() or "", - }, - ) - - def gen_full_js(self): - """ Generate the full script tag containing the JS code """ - self.create_js() - self.fill_js() - self.get_script() - - def create_js(self): - """ Generate a template for the whole script to use depending on - gen_select and multiple """ - if self.gen_select: - if self.multiple: - self.js_script = ( - '$( "#{input_id}" ).ready( function() {{' - " var choices_{f_name} = {choices};" - " {del_select}" - " var engine_{f_name};" - " var setup_{f_name} = function() {{" - " engine_{f_name} = {engine};" - ' $( "#{input_id}" ).tokenfield( "destroy" );' - ' $( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});' - " }};" - ' $( "#{input_id}" ).bind( "tokenfield:createtoken", {tok_create} );' - ' $( "#{input_id}" ).bind( "tokenfield:edittoken", {tok_edit} );' - ' $( "#{input_id}" ).bind( "tokenfield:removetoken", {tok_remove} );' - " {tok_updates}" - " setup_{f_name}();" - " {tok_init_input}" - "}} );" - ) - else: - self.js_script = ( - '$( "#{input_id}" ).ready( function() {{' - " var choices_{f_name} = {choices};" - " {del_select}" - " {gen_hidden}" - " var engine_{f_name};" - " var setup_{f_name} = function() {{" - " engine_{f_name} = {engine};" - ' $( "#{input_id}" ).typeahead( "destroy" );' - ' $( "#{input_id}" ).typeahead( {datasets} );' - " }};" - ' $( "#{input_id}" ).bind( "typeahead:select", {typ_select} );' - ' $( "#{input_id}" ).bind( "typeahead:change", {typ_change} );' - " {typ_updates}" - " setup_{f_name}();" - " {typ_init_input}" - "}} );" - ) - else: - if self.multiple: - self.js_script = ( - "var choices_{f_name} = {choices};" - "var engine_{f_name};" - "var setup_{f_name} = function() {{" - " engine_{f_name} = {engine};" - ' $( "#{input_id}" ).tokenfield( "destroy" );' - ' $( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});' - "}};" - '$( "#{input_id}" ).bind( "tokenfield:createtoken", {tok_create} );' - '$( "#{input_id}" ).bind( "tokenfield:edittoken", {tok_edit} );' - '$( "#{input_id}" ).bind( "tokenfield:removetoken", {tok_remove} );' - "{tok_updates}" - '$( "#{input_id}" ).ready( function() {{' - " setup_{f_name}();" - " {tok_init_input}" - "}} );" - ) - else: - self.js_script = ( - "var choices_{f_name} ={choices};" - "var engine_{f_name};" - "var setup_{f_name} = function() {{" - " engine_{f_name} = {engine};" - ' $( "#{input_id}" ).typeahead( "destroy" );' - ' $( "#{input_id}" ).typeahead( {datasets} );' - "}};" - '$( "#{input_id}" ).bind( "typeahead:select", {typ_select} );' - '$( "#{input_id}" ).bind( "typeahead:change", {typ_change} );' - "{typ_updates}" - '$( "#{input_id}" ).ready( function() {{' - " setup_{f_name}();" - " {typ_init_input}" - "}} );" - ) - - # Make sure the visible element doesn't have the same name as the hidden elements - # Otherwise, in the POST request, they collide and an incoherent value is sent - self.js_script += ( - '$( "#{input_id}" ).ready( function() {{' - ' $( "#{input_id}" ).attr("name", "mbf_{f_name}");' - "}} );" - ) - - def fill_js(self): - """ Fill the template with the correct values """ - self.js_script = self.js_script.format( - f_name=self.name, - choices=self.choices, - del_select=self.del_select(), - gen_hidden=self.gen_hidden(), - engine=self.engine, - input_id=self.input_id, - datasets=self.datasets, - typ_select=self.typeahead_select(), - typ_change=self.typeahead_change(), - tok_create=self.tokenfield_create(), - tok_edit=self.tokenfield_edit(), - tok_remove=self.tokenfield_remove(), - typ_updates=self.typeahead_updates(), - tok_updates=self.tokenfield_updates(), - tok_init_input=self.tokenfield_init_input(), - typ_init_input=self.typeahead_init_input(), - ) - - def get_script(self): - """ Insert the JS code inside a script tag """ - self.js_script = render_tag("script", content=mark_safe(self.js_script)) - - def del_select(self): - """ JS code to delete the select if it has been generated and replace - it with an input. """ - return ( - 'var p = $("#{select_id}").parent()[0];' - "var new_input = `{replace_input}`;" - "p.innerHTML = new_input;" - ).format(select_id=self.input_id, replace_input=self.replace_input) - - def gen_hidden(self): - """ JS code to add a hidden tag to store the value. """ - return ( - 'var d = $("#{div2_id}")[0];' - 'var i = document.createElement("input");' - 'i.id = "{hidden_id}";' - 'i.name = "{html_name}";' - 'i.value = "";' - 'i.type = "hidden";' - "d.appendChild(i);" - ).format( - div2_id=self.div2_id, - hidden_id=self.hidden_id, - html_name=self.bound.html_name, - ) - - def typeahead_init_input(self): - """ JS code to init the fields values """ - init_key = self.bound.value() or '""' - return ( - '$( "#{input_id}" ).typeahead("val", {init_val});' - '$( "#{hidden_id}" ).val( {init_key} );' - ).format( - input_id=self.input_id, - init_val='""' - if init_key == '""' - else "engine_{name}.get( {init_key} )[0].value".format( - name=self.name, init_key=init_key - ), - init_key=init_key, - hidden_id=self.hidden_id, - ) - - def typeahead_reset_input(self): - """ JS code to reset the fields values """ - return ( - '$( "#{input_id}" ).typeahead("val", "");' '$( "#{hidden_id}" ).val( "" );' - ).format(input_id=self.input_id, hidden_id=self.hidden_id) - - def typeahead_select(self): - """ JS code to create the function triggered when an item is selected - through typeahead """ - return ( - "function(evt, item) {{" - ' $( "#{hidden_id}" ).val( item.key );' - ' $( "#{hidden_id}" ).change();' - " return item;" - "}}" - ).format(hidden_id=self.hidden_id) - - def typeahead_change(self): - """ JS code of the function triggered when an item is changed (i.e. - looses focus and value has changed since the moment it gained focus ) - """ - return ( - "function(evt) {{" - ' if ( $( "#{input_id}" ).typeahead( "val" ) === "" ) {{' - ' $( "#{hidden_id}" ).val( "" );' - ' $( "#{hidden_id}" ).change();' - " }}" - "}}" - ).format(input_id=self.input_id, hidden_id=self.hidden_id) - - def typeahead_updates(self): - """ JS code for binding external fields changes with a reset """ - reset_input = self.typeahead_reset_input() - updates = [ - ( - '$( "#{u_id}" ).change( function() {{' - " setup_{name}();" - " {reset_input}" - "}} );" - ).format(u_id=u_id, name=self.name, reset_input=reset_input) - for u_id in self.update_on - ] - return "".join(updates) - - def tokenfield_init_input(self): - """ JS code to init the fields values """ - init_key = self.bound.value() or '""' - return ('$( "#{input_id}" ).tokenfield("setTokens", {init_val});').format( - input_id=self.input_id, - init_val='""' - if init_key == '""' - else ( - "engine_{name}.get( {init_key} ).map(" - " function(o) {{ return o.value; }}" - ")" - ).format(name=self.name, init_key=init_key), - ) - - def tokenfield_reset_input(self): - """ JS code to reset the fields values """ - return ('$( "#{input_id}" ).tokenfield("setTokens", "");').format( - input_id=self.input_id - ) - - def tokenfield_create(self): - """ JS code triggered when a new token is created in tokenfield. """ - return ( - "function(evt) {{" - " var k = evt.attrs.key;" - " if (!k) {{" - " var data = evt.attrs.value;" - " var i = 0;" - " while ( i Date: Mon, 28 Dec 2020 19:46:50 +0100 Subject: [PATCH 415/490] Add autocomplete on preferences pannel --- preferences/forms.py | 22 ++++++++++++++++--- .../preferences/edit_preferences.html | 4 ++-- .../templates/preferences/preferences.html | 4 ++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/preferences/forms.py b/preferences/forms.py index a0880fd6..fdc752ab 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -29,7 +29,7 @@ from django.forms import ModelForm, Form from django.db.models import Q from django import forms from django.utils.translation import ugettext_lazy as _ -from re2o.mixins import FormRevMixin +from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin from .models import ( OptionalUser, OptionalMachine, @@ -168,6 +168,11 @@ class EditAssoOptionForm(ModelForm): class Meta: model = AssoOption fields = "__all__" + widgets = { + "utilisateur_asso": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -254,6 +259,11 @@ class MandateForm(ModelForm): class Meta: model = Mandate fields = "__all__" + widgets = { + "president": AutocompleteModelMixin( + url="/users/user-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -368,7 +378,9 @@ class RadiusKeyForm(FormRevMixin, ModelForm): """Form used to add and edit RADIUS keys.""" members = forms.ModelMultipleChoiceField( - queryset=Switch.objects.all(), required=False + queryset=Switch.objects.all(), + required=False, + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), ) class Meta: @@ -391,7 +403,11 @@ class RadiusKeyForm(FormRevMixin, ModelForm): class SwitchManagementCredForm(FormRevMixin, ModelForm): """Form used to add and edit switch management credentials.""" - members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False) + members = forms.ModelMultipleChoiceField( + Switch.objects.all(), + required=False, + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + ) class Meta: model = SwitchManagementCred diff --git a/preferences/templates/preferences/edit_preferences.html b/preferences/templates/preferences/edit_preferences.html index 2d9e1a62..c0fe2256 100644 --- a/preferences/templates/preferences/edit_preferences.html +++ b/preferences/templates/preferences/edit_preferences.html @@ -24,19 +24,19 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Preferences" %}{% endblock %} {% block content %} {% bootstrap_form_errors options %} +{{ options.media }}

    {% trans "Editing of preferences" %}

    {% csrf_token %} - {% massive_bootstrap_form options 'utilisateur_asso,automatic_provision_switchs' %} + {% bootstrap_form options %} {% if formset %} {{ formset.management_form }} {% for f in formset %} diff --git a/preferences/templates/preferences/preferences.html b/preferences/templates/preferences/preferences.html index dda6ddfa..d1409cb8 100644 --- a/preferences/templates/preferences/preferences.html +++ b/preferences/templates/preferences/preferences.html @@ -25,20 +25,20 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load bootstrap3 %} {% load i18n %} -{% load massive_bootstrap_form %} {% block title %}{% trans "Preferences" %}{% endblock %} {% block content %} {% if preferenceform %} {% bootstrap_form_errors preferenceform %} +{{ preferenceform.media }} {% endif %} {% csrf_token %} {% if preferenceform %} - {% massive_bootstrap_form preferenceform 'members,president' %} + {% bootstrap_form preferenceform %} {% endif %} {% bootstrap_button action_name button_type="submit" icon='ok' button_class='btn-success' %} From e68c4a146988deaeb2040462cc2f38eb5e9c4d97 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 20:01:02 +0100 Subject: [PATCH 416/490] Add iptype autocomplete search --- machines/forms.py | 5 +++++ machines/templates/machines/machine.html | 1 + machines/urls.py | 1 + machines/views_autocomplete.py | 5 +++++ 4 files changed, 12 insertions(+) diff --git a/machines/forms.py b/machines/forms.py index f663f5a9..3f6a3864 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -212,6 +212,11 @@ class MachineTypeForm(FormRevMixin, ModelForm): class Meta: model = MachineType fields = ["name", "ip_type"] + widgets = { + "ip_type": AutocompleteModelMixin( + url="/machines/iptype-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) diff --git a/machines/templates/machines/machine.html b/machines/templates/machines/machine.html index 2cd97097..bc1cdbeb 100644 --- a/machines/templates/machines/machine.html +++ b/machines/templates/machines/machine.html @@ -47,6 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endif %} {% if machinetypeform %} {% bootstrap_form_errors machinetypeform %} + {{ machinetypeform.media }} {% endif %} {% if extensionform %} {% bootstrap_form_errors extensionform %} diff --git a/machines/urls.py b/machines/urls.py index 387bdae1..98a6695f 100644 --- a/machines/urls.py +++ b/machines/urls.py @@ -159,6 +159,7 @@ urlpatterns = [ url(r'^interface-autocomplete/$', views_autocomplete.InterfaceAutocomplete.as_view(), name='interface-autocomplete',), url(r'^machine-autocomplete/$', views_autocomplete.MachineAutocomplete.as_view(), name='machine-autocomplete',), url(r'^machinetype-autocomplete/$', views_autocomplete.MachineTypeAutocomplete.as_view(), name='machinetype-autocomplete',), + url(r'^iptype-autocomplete/$', views_autocomplete.IpTypeAutocomplete.as_view(), name='iptype-autocomplete',), url(r'^extension-autocomplete/$', views_autocomplete.ExtensionAutocomplete.as_view(), name='extension-autocomplete',), url(r'^domain-autocomplete/$', views_autocomplete.DomainAutocomplete.as_view(), name='domain-autocomplete',), url(r'^ouvertureportlist-autocomplete/$', views_autocomplete.OuverturePortListAutocomplete.as_view(), name='ouvertureportlist-autocomplete',), diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index a79fd276..e68a11c9 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -39,6 +39,7 @@ from .models import ( Machine, Vlan, MachineType, + IpType, Extension, Domain, OuverturePortList, @@ -64,6 +65,10 @@ class MachineTypeAutocomplete(AutocompleteViewMixin): obj_type = MachineType +class IpTypeAutocomplete(AutocompleteViewMixin): + obj_type = IpType + + class ExtensionAutocomplete(AutocompleteViewMixin): obj_type = Extension From faac45dec52354e254f32e0590353b7429cad8e3 Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 20:01:39 +0100 Subject: [PATCH 417/490] Fix switchs management autocomplete form --- preferences/forms.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/preferences/forms.py b/preferences/forms.py index fdc752ab..a7d03162 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -108,12 +108,19 @@ class EditOptionalTopologieForm(ModelForm): """Form used to edit the configuration of switches.""" automatic_provision_switchs = forms.ModelMultipleChoiceField( - Switch.objects.all(), required=False + Switch.objects.all(), + required=False, + widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), ) class Meta: model = OptionalTopologie fields = "__all__" + widgets = { + "switchs_ip_type": AutocompleteModelMixin( + url="/machines/iptype-autocomplete", + ), + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) From 033921669edb9a9b38bbff764ee4b3268b2bffbd Mon Sep 17 00:00:00 2001 From: chirac Date: Mon, 28 Dec 2020 21:06:24 +0100 Subject: [PATCH 418/490] Autocomplete for club edition --- users/forms.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/users/forms.py b/users/forms.py index 3bf19dcc..195807a3 100644 --- a/users/forms.py +++ b/users/forms.py @@ -60,7 +60,7 @@ from topologie.models import Port from preferences.models import OptionalUser from re2o.utils import remove_user_room from re2o.base import get_input_formats_help_text -from re2o.mixins import FormRevMixin, AutocompleteModelMixin +from re2o.mixins import FormRevMixin, AutocompleteMultipleModelMixin, AutocompleteModelMixin from re2o.field_permissions import FieldPermissionFormMixin from preferences.models import GeneralOption @@ -356,6 +356,9 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): ), "room": AutocompleteModelMixin( url="/topologie/room-autocomplete", + attrs = { + "data-minimum-input-length": 3 # Only trigger autocompletion after 3 characters have been typed + } ), "shell": AutocompleteModelMixin( url="/users/shell-autocomplete", @@ -659,6 +662,14 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm): class Meta: model = Club fields = ["administrators", "members"] + widgets = { + "administrators": AutocompleteMultipleModelMixin( + url="/users/adherent-autocomplete", + ), + "members": AutocompleteMultipleModelMixin( + url="/users/adherent-autocomplete", + ) + } def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) From 773892ae1c517a3f269e5d6259606ac087eabd42 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 28 Dec 2020 22:24:35 +0100 Subject: [PATCH 419/490] Add django-autocomplete-light dependency --- pip_requirements.txt | 2 ++ static/css/autocomplete.css | 49 +++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 static/css/autocomplete.css diff --git a/pip_requirements.txt b/pip_requirements.txt index 2d0aba41..148a3b5b 100644 --- a/pip_requirements.txt +++ b/pip_requirements.txt @@ -1,2 +1,4 @@ django-bootstrap3==11.1.0 django-macaddress==1.6.0 +#django-autocomplete-light==3.2.10 # Until Django 2.0+ +django-autocomplete-light diff --git a/static/css/autocomplete.css b/static/css/autocomplete.css new file mode 100644 index 00000000..dea68af3 --- /dev/null +++ b/static/css/autocomplete.css @@ -0,0 +1,49 @@ +/* +Don't blame me for all the '!important's +See github.com/yourlabs/django-autocomplete-light/issues/1149 +*/ + +/* dal bootstrap css fix */ +.select2-container { + width: 100% !important; + min-width: 10em !important; +} + +/* django-addanother bootstrap css fix */ +.related-widget-wrapper{ + padding-right: 16px; + position: relative; +} + +.related-widget-wrapper-link{ + position: absolute; + top: 3px; + right: 0px; +} + +.select2-container .select2-selection--single { + height: 34px !important; + padding-right: 20px; +} + +.select2-container--default .select2-selection--single .select2-selection__rendered { + line-height: 100% !important; + display: inline !important; + overflow-x: hidden !important; + overflow-y: auto !important; +} +.select2-container .select2-selection--multiple { + min-height: 45px !important; + padding-right: 20px; +} + +.select2-container--default .select2-selection--multiple .select2-selection__rendered { + height: 100% !important; + display: inline !imoortant; + overflow-x: hidden !important; + overflow-y: auto !important; +} + +.select2-container .select2-selection--multiple .select2-selection__rendered { + overflow: auto !important; +} From d022b1f251c7ceb764f91f7f55045ae8fe831307 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 28 Dec 2020 22:28:37 +0100 Subject: [PATCH 420/490] Remove legacy typeahead files --- static/css/bootstrap-tokenfield.css | 210 -- static/css/typeaheadjs.css | 93 - static/js/bootstrap-tokenfield/LICENSE.md | 23 - .../bootstrap-tokenfield.js | 1042 ------- static/js/typeahead/LICENSE | 19 - static/js/typeahead/typeahead.js | 2451 ----------------- templates/base.html | 7 +- 7 files changed, 2 insertions(+), 3843 deletions(-) delete mode 100644 static/css/bootstrap-tokenfield.css delete mode 100644 static/css/typeaheadjs.css delete mode 100644 static/js/bootstrap-tokenfield/LICENSE.md delete mode 100644 static/js/bootstrap-tokenfield/bootstrap-tokenfield.js delete mode 100644 static/js/typeahead/LICENSE delete mode 100644 static/js/typeahead/typeahead.js diff --git a/static/css/bootstrap-tokenfield.css b/static/css/bootstrap-tokenfield.css deleted file mode 100644 index ae12c1b7..00000000 --- a/static/css/bootstrap-tokenfield.css +++ /dev/null @@ -1,210 +0,0 @@ -/*! - * bootstrap-tokenfield - * https://github.com/sliptree/bootstrap-tokenfield - * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT - */ -@-webkit-keyframes blink { - 0% { - border-color: #ededed; - } - 100% { - border-color: #b94a48; - } -} -@-moz-keyframes blink { - 0% { - border-color: #ededed; - } - 100% { - border-color: #b94a48; - } -} -@keyframes blink { - 0% { - border-color: #ededed; - } - 100% { - border-color: #b94a48; - } -} -.tokenfield { - height: auto; - min-height: 34px; - padding-bottom: 0px; -} -.tokenfield.focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6); -} -.tokenfield .token { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - display: inline-block; - border: 1px solid #d9d9d9; - background-color: #ededed; - white-space: nowrap; - margin: -1px 5px 5px 0; - height: 22px; - vertical-align: top; - cursor: default; -} -.tokenfield .token:hover { - border-color: #b9b9b9; -} -.tokenfield .token.active { - border-color: #52a8ec; - border-color: rgba(82, 168, 236, 0.8); -} -.tokenfield .token.duplicate { - border-color: #ebccd1; - -webkit-animation-name: blink; - animation-name: blink; - -webkit-animation-duration: 0.1s; - animation-duration: 0.1s; - -webkit-animation-direction: normal; - animation-direction: normal; - -webkit-animation-timing-function: ease; - animation-timing-function: ease; - -webkit-animation-iteration-count: infinite; - animation-iteration-count: infinite; -} -.tokenfield .token.invalid { - background: none; - border: 1px solid transparent; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-radius: 0; - border-bottom: 1px dotted #d9534f; -} -.tokenfield .token.invalid.active { - background: #ededed; - border: 1px solid #ededed; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.tokenfield .token .token-label { - display: inline-block; - overflow: hidden; - text-overflow: ellipsis; - padding-left: 4px; - vertical-align: top; -} -.tokenfield .token .close { - font-family: Arial; - display: inline-block; - line-height: 100%; - font-size: 1.1em; - line-height: 1.49em; - margin-left: 5px; - float: none; - height: 100%; - vertical-align: top; - padding-right: 4px; -} -.tokenfield .token-input { - background: none; - width: 60px; - min-width: 60px; - border: 0; - height: 20px; - padding: 0; - margin-bottom: 6px; - -webkit-box-shadow: none; - box-shadow: none; -} -.tokenfield .token-input:focus { - border-color: transparent; - outline: 0; - /* IE6-9 */ - -webkit-box-shadow: none; - box-shadow: none; -} -.tokenfield.disabled { - cursor: not-allowed; - background-color: #eeeeee; -} -.tokenfield.disabled .token-input { - cursor: not-allowed; -} -.tokenfield.disabled .token:hover { - cursor: not-allowed; - border-color: #d9d9d9; -} -.tokenfield.disabled .token:hover .close { - cursor: not-allowed; - opacity: 0.2; - filter: alpha(opacity=20); -} -.has-warning .tokenfield.focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; -} -.has-error .tokenfield.focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; -} -.has-success .tokenfield.focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; -} -.tokenfield.input-sm, -.input-group-sm .tokenfield { - min-height: 30px; - padding-bottom: 0px; -} -.input-group-sm .token, -.tokenfield.input-sm .token { - height: 20px; - margin-bottom: 4px; -} -.input-group-sm .token-input, -.tokenfield.input-sm .token-input { - height: 18px; - margin-bottom: 5px; -} -.tokenfield.input-lg, -.input-group-lg .tokenfield { - height: auto; - min-height: 45px; - padding-bottom: 4px; -} -.input-group-lg .token, -.tokenfield.input-lg .token { - height: 25px; -} -.input-group-lg .token-label, -.tokenfield.input-lg .token-label { - line-height: 23px; -} -.input-group-lg .token .close, -.tokenfield.input-lg .token .close { - line-height: 1.3em; -} -.input-group-lg .token-input, -.tokenfield.input-lg .token-input { - height: 23px; - line-height: 23px; - margin-bottom: 6px; - vertical-align: top; -} -.tokenfield.rtl { - direction: rtl; - text-align: right; -} -.tokenfield.rtl .token { - margin: -1px 0 5px 5px; -} -.tokenfield.rtl .token .token-label { - padding-left: 0px; - padding-right: 4px; -} diff --git a/static/css/typeaheadjs.css b/static/css/typeaheadjs.css deleted file mode 100644 index 64c10736..00000000 --- a/static/css/typeaheadjs.css +++ /dev/null @@ -1,93 +0,0 @@ -span.twitter-typeahead .tt-menu, -span.twitter-typeahead .tt-dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - list-style: none; - font-size: 14px; - text-align: left; - background-color: #ffffff; - border: 1px solid #cccccc; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); - background-clip: padding-box; -} -span.twitter-typeahead .tt-suggestion { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333333; - white-space: nowrap; -} -span.twitter-typeahead .tt-suggestion.tt-cursor, -span.twitter-typeahead .tt-suggestion:hover, -span.twitter-typeahead .tt-suggestion:focus { - color: #ffffff; - text-decoration: none; - outline: 0; - background-color: #337ab7; -} -.input-group.input-group-lg span.twitter-typeahead .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.input-group.input-group-sm span.twitter-typeahead .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -span.twitter-typeahead { - width: 100%; -} -.input-group span.twitter-typeahead { - display: block !important; - height: 34px; -} -.input-group span.twitter-typeahead .tt-menu, -.input-group span.twitter-typeahead .tt-dropdown-menu { - top: 32px !important; -} -.input-group span.twitter-typeahead:not(:first-child):not(:last-child) .form-control { - border-radius: 0; -} -.input-group span.twitter-typeahead:first-child .form-control { - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group span.twitter-typeahead:last-child .form-control { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.input-group.input-group-sm span.twitter-typeahead { - height: 30px; -} -.input-group.input-group-sm span.twitter-typeahead .tt-menu, -.input-group.input-group-sm span.twitter-typeahead .tt-dropdown-menu { - top: 30px !important; -} -.input-group.input-group-lg span.twitter-typeahead { - height: 46px; -} -.input-group.input-group-lg span.twitter-typeahead .tt-menu, -.input-group.input-group-lg span.twitter-typeahead .tt-dropdown-menu { - top: 46px !important; -} diff --git a/static/js/bootstrap-tokenfield/LICENSE.md b/static/js/bootstrap-tokenfield/LICENSE.md deleted file mode 100644 index 2449b356..00000000 --- a/static/js/bootstrap-tokenfield/LICENSE.md +++ /dev/null @@ -1,23 +0,0 @@ -#### Sliptree -- by Illimar Tambek for [Sliptree](http://sliptree.com) -- Copyright (c) 2013 by Sliptree - -Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/static/js/bootstrap-tokenfield/bootstrap-tokenfield.js b/static/js/bootstrap-tokenfield/bootstrap-tokenfield.js deleted file mode 100644 index 5b2759d4..00000000 --- a/static/js/bootstrap-tokenfield/bootstrap-tokenfield.js +++ /dev/null @@ -1,1042 +0,0 @@ -/*! - * bootstrap-tokenfield - * https://github.com/sliptree/bootstrap-tokenfield - * Copyright 2013-2014 Sliptree and other contributors; Licensed MIT - */ - -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as an anonymous module. - define(['jquery'], factory); - } else if (typeof exports === 'object') { - // For CommonJS and CommonJS-like environments where a window with jQuery - // is present, execute the factory with the jQuery instance from the window object - // For environments that do not inherently posses a window with a document - // (such as Node.js), expose a Tokenfield-making factory as module.exports - // This accentuates the need for the creation of a real window or passing in a jQuery instance - // e.g. require("bootstrap-tokenfield")(window); or require("bootstrap-tokenfield")($); - module.exports = global.window && global.window.$ ? - factory( global.window.$ ) : - function( input ) { - if ( !input.$ && !input.fn ) { - throw new Error( "Tokenfield requires a window object with jQuery or a jQuery instance" ); - } - return factory( input.$ || input ); - }; - } else { - // Browser globals - factory(jQuery, window); - } -}(function ($, window) { - - "use strict"; // jshint ;_; - - /* TOKENFIELD PUBLIC CLASS DEFINITION - * ============================== */ - - var Tokenfield = function (element, options) { - var _self = this - - this.$element = $(element) - this.textDirection = this.$element.css('direction'); - - // Extend options - this.options = $.extend(true, {}, $.fn.tokenfield.defaults, { tokens: this.$element.val() }, this.$element.data(), options) - - // Setup delimiters and trigger keys - this._delimiters = (typeof this.options.delimiter === 'string') ? [this.options.delimiter] : this.options.delimiter - this._triggerKeys = $.map(this._delimiters, function (delimiter) { - return delimiter.charCodeAt(0); - }); - this._firstDelimiter = this._delimiters[0]; - - // Check for whitespace, dash and special characters - var whitespace = $.inArray(' ', this._delimiters) - , dash = $.inArray('-', this._delimiters) - - if (whitespace >= 0) - this._delimiters[whitespace] = '\\s' - - if (dash >= 0) { - delete this._delimiters[dash] - this._delimiters.unshift('-') - } - - var specialCharacters = ['\\', '$', '[', '{', '^', '.', '|', '?', '*', '+', '(', ')'] - $.each(this._delimiters, function (index, character) { - var pos = $.inArray(character, specialCharacters) - if (pos >= 0) _self._delimiters[index] = '\\' + character; - }); - - // Store original input width - var elRules = (window && typeof window.getMatchedCSSRules === 'function') ? window.getMatchedCSSRules( element ) : null - , elStyleWidth = element.style.width - , elCSSWidth - , elWidth = this.$element.width() - - if (elRules) { - $.each( elRules, function (i, rule) { - if (rule.style.width) { - elCSSWidth = rule.style.width; - } - }); - } - - // Move original input out of the way - var hidingPosition = $('body').css('direction') === 'rtl' ? 'right' : 'left', - originalStyles = { position: this.$element.css('position') }; - originalStyles[hidingPosition] = this.$element.css(hidingPosition); - - this.$element - .data('original-styles', originalStyles) - .data('original-tabindex', this.$element.prop('tabindex')) - .css('position', 'absolute') - .css(hidingPosition, '-10000px') - .prop('tabindex', -1) - - // Create a wrapper - this.$wrapper = $('
    ') - if (this.$element.hasClass('input-lg')) this.$wrapper.addClass('input-lg') - if (this.$element.hasClass('input-sm')) this.$wrapper.addClass('input-sm') - if (this.textDirection === 'rtl') this.$wrapper.addClass('rtl') - - // Create a new input - var id = this.$element.prop('id') || new Date().getTime() + '' + Math.floor((1 + Math.random()) * 100) - this.$input = $('') - .appendTo( this.$wrapper ) - .prop( 'placeholder', this.$element.prop('placeholder') ) - .prop( 'id', id + '-tokenfield' ) - .prop( 'tabindex', this.$element.data('original-tabindex') ) - - // Re-route original input label to new input - var $label = $( 'label[for="' + this.$element.prop('id') + '"]' ) - if ( $label.length ) { - $label.prop( 'for', this.$input.prop('id') ) - } - - // Set up a copy helper to handle copy & paste - this.$copyHelper = $('').css('position', 'absolute').css(hidingPosition, '-10000px').prop('tabindex', -1).prependTo( this.$wrapper ) - - // Set wrapper width - if (elStyleWidth) { - this.$wrapper.css('width', elStyleWidth); - } - else if (elCSSWidth) { - this.$wrapper.css('width', elCSSWidth); - } - // If input is inside inline-form with no width set, set fixed width - else if (this.$element.parents('.form-inline').length) { - this.$wrapper.width( elWidth ) - } - - // Set tokenfield disabled, if original or fieldset input is disabled - if (this.$element.prop('disabled') || this.$element.parents('fieldset[disabled]').length) { - this.disable(); - } - - // Set tokenfield readonly, if original input is readonly - if (this.$element.prop('readonly')) { - this.readonly(); - } - - // Set up mirror for input auto-sizing - this.$mirror = $(''); - this.$input.css('min-width', this.options.minWidth + 'px') - $.each([ - 'fontFamily', - 'fontSize', - 'fontWeight', - 'fontStyle', - 'letterSpacing', - 'textTransform', - 'wordSpacing', - 'textIndent' - ], function (i, val) { - _self.$mirror[0].style[val] = _self.$input.css(val); - }); - this.$mirror.appendTo( 'body' ) - - // Insert tokenfield to HTML - this.$wrapper.insertBefore( this.$element ) - this.$element.prependTo( this.$wrapper ) - - // Calculate inner input width - this.update() - - // Create initial tokens, if any - this.setTokens(this.options.tokens, false, ! this.$element.val() && this.options.tokens ) - - // Start listening to events - this.listen() - - // Initialize autocomplete, if necessary - if ( ! $.isEmptyObject( this.options.autocomplete ) ) { - var side = this.textDirection === 'rtl' ? 'right' : 'left' - , autocompleteOptions = $.extend({ - minLength: this.options.showAutocompleteOnFocus ? 0 : null, - position: { my: side + " top", at: side + " bottom", of: this.$wrapper } - }, this.options.autocomplete ) - - this.$input.autocomplete( autocompleteOptions ) - } - - // Initialize typeahead, if necessary - if ( ! $.isEmptyObject( this.options.typeahead ) ) { - - var typeaheadOptions = this.options.typeahead - , defaults = { - minLength: this.options.showAutocompleteOnFocus ? 0 : null - } - , args = $.isArray( typeaheadOptions ) ? typeaheadOptions : [typeaheadOptions, typeaheadOptions] - - args[0] = $.extend( {}, defaults, args[0] ) - - this.$input.typeahead.apply( this.$input, args ) - this.typeahead = true - } - } - - Tokenfield.prototype = { - - constructor: Tokenfield - - , createToken: function (attrs, triggerChange) { - var _self = this - - if (typeof attrs === 'string') { - attrs = { value: attrs, label: attrs } - } else { - // Copy objects to prevent contamination of data sources. - attrs = $.extend( {}, attrs ) - } - - if (typeof triggerChange === 'undefined') { - triggerChange = true - } - - // Normalize label and value - attrs.value = $.trim(attrs.value.toString()); - attrs.label = attrs.label && attrs.label.length ? $.trim(attrs.label) : attrs.value - - // Bail out if has no value or label, or label is too short - if (!attrs.value.length || !attrs.label.length || attrs.label.length <= this.options.minLength) return - - // Bail out if maximum number of tokens is reached - if (this.options.limit && this.getTokens().length >= this.options.limit) return - - // Allow changing token data before creating it - var createEvent = $.Event('tokenfield:createtoken', { attrs: attrs }) - this.$element.trigger(createEvent) - - // Bail out if there if attributes are empty or event was defaultPrevented - if (!createEvent.attrs || createEvent.isDefaultPrevented()) return - - var $token = $('
    ') - .append('') - .append('×') - .data('attrs', attrs) - - // Insert token into HTML - if (this.$input.hasClass('tt-input')) { - // If the input has typeahead enabled, insert token before it's parent - this.$input.parent().before( $token ) - } else { - this.$input.before( $token ) - } - - // Temporarily set input width to minimum - this.$input.css('width', this.options.minWidth + 'px') - - var $tokenLabel = $token.find('.token-label') - , $closeButton = $token.find('.close') - - // Determine maximum possible token label width - if (!this.maxTokenWidth) { - this.maxTokenWidth = - this.$wrapper.width() - $closeButton.outerWidth() - - parseInt($closeButton.css('margin-left'), 10) - - parseInt($closeButton.css('margin-right'), 10) - - parseInt($token.css('border-left-width'), 10) - - parseInt($token.css('border-right-width'), 10) - - parseInt($token.css('padding-left'), 10) - - parseInt($token.css('padding-right'), 10) - parseInt($tokenLabel.css('border-left-width'), 10) - - parseInt($tokenLabel.css('border-right-width'), 10) - - parseInt($tokenLabel.css('padding-left'), 10) - - parseInt($tokenLabel.css('padding-right'), 10) - parseInt($tokenLabel.css('margin-left'), 10) - - parseInt($tokenLabel.css('margin-right'), 10) - } - - $tokenLabel.css('max-width', this.maxTokenWidth) - if (this.options.html) - $tokenLabel.html(attrs.label) - else - $tokenLabel.text(attrs.label) - - // Listen to events on token - $token - .on('mousedown', function (e) { - if (_self._disabled || _self._readonly) return false - _self.preventDeactivation = true - }) - .on('click', function (e) { - if (_self._disabled || _self._readonly) return false - _self.preventDeactivation = false - - if (e.ctrlKey || e.metaKey) { - e.preventDefault() - return _self.toggle( $token ) - } - - _self.activate( $token, e.shiftKey, e.shiftKey ) - }) - .on('dblclick', function (e) { - if (_self._disabled || _self._readonly || !_self.options.allowEditing ) return false - _self.edit( $token ) - }) - - $closeButton - .on('click', $.proxy(this.remove, this)) - - // Trigger createdtoken event on the original field - // indicating that the token is now in the DOM - this.$element.trigger($.Event('tokenfield:createdtoken', { - attrs: attrs, - relatedTarget: $token.get(0) - })) - - // Trigger change event on the original field - if (triggerChange) { - this.$element.val( this.getTokensList() ).trigger( $.Event('change', { initiator: 'tokenfield' }) ) - } - - // Update tokenfield dimensions - var _self = this - setTimeout(function () { - _self.update() - }, 0) - - // Return original element - return this.$element.get(0) - } - - , setTokens: function (tokens, add, triggerChange) { - if (!add) this.$wrapper.find('.token').remove() - - if (!tokens) return - - if (typeof triggerChange === 'undefined') { - triggerChange = true - } - - if (typeof tokens === 'string') { - if (this._delimiters.length) { - // Split based on delimiters - tokens = tokens.split( new RegExp( '[' + this._delimiters.join('') + ']' ) ) - } else { - tokens = [tokens]; - } - } - - var _self = this - $.each(tokens, function (i, attrs) { - _self.createToken(attrs, triggerChange) - }) - - return this.$element.get(0) - } - - , getTokenData: function($token) { - var data = $token.map(function() { - var $token = $(this); - return $token.data('attrs') - }).get(); - - if (data.length == 1) { - data = data[0]; - } - - return data; - } - - , getTokens: function(active) { - var self = this - , tokens = [] - , activeClass = active ? '.active' : '' // get active tokens only - this.$wrapper.find( '.token' + activeClass ).each( function() { - tokens.push( self.getTokenData( $(this) ) ) - }) - return tokens - } - - , getTokensList: function(delimiter, beautify, active) { - delimiter = delimiter || this._firstDelimiter - beautify = ( typeof beautify !== 'undefined' && beautify !== null ) ? beautify : this.options.beautify - - var separator = delimiter + ( beautify && delimiter !== ' ' ? ' ' : '') - return $.map( this.getTokens(active), function (token) { - return token.value - }).join(separator) - } - - , getInput: function() { - return this.$input.val() - } - - , setInput: function (val) { - if (this.$input.hasClass('tt-input')) { - // Typeahead acts weird when simply setting input value to empty, - // so we set the query to empty instead - this.$input.typeahead('val', val) - } else { - this.$input.val(val) - } - } - - , listen: function () { - var _self = this - - this.$element - .on('change', $.proxy(this.change, this)) - - this.$wrapper - .on('mousedown',$.proxy(this.focusInput, this)) - - this.$input - .on('focus', $.proxy(this.focus, this)) - .on('blur', $.proxy(this.blur, this)) - .on('paste', $.proxy(this.paste, this)) - .on('keydown', $.proxy(this.keydown, this)) - .on('keypress', $.proxy(this.keypress, this)) - .on('keyup', $.proxy(this.keyup, this)) - - this.$copyHelper - .on('focus', $.proxy(this.focus, this)) - .on('blur', $.proxy(this.blur, this)) - .on('keydown', $.proxy(this.keydown, this)) - .on('keyup', $.proxy(this.keyup, this)) - - // Secondary listeners for input width calculation - this.$input - .on('keypress', $.proxy(this.update, this)) - .on('keyup', $.proxy(this.update, this)) - - this.$input - .on('autocompletecreate', function() { - // Set minimum autocomplete menu width - var $_menuElement = $(this).data('ui-autocomplete').menu.element - - var minWidth = _self.$wrapper.outerWidth() - - parseInt( $_menuElement.css('border-left-width'), 10 ) - - parseInt( $_menuElement.css('border-right-width'), 10 ) - - $_menuElement.css( 'min-width', minWidth + 'px' ) - }) - .on('autocompleteselect', function (e, ui) { - if (_self.createToken( ui.item )) { - _self.$input.val('') - if (_self.$input.data( 'edit' )) { - _self.unedit(true) - } - } - return false - }) - .on('typeahead:selected typeahead:autocompleted', function (e, datum, dataset) { - // Create token - if (_self.createToken( datum )) { - _self.$input.typeahead('val', '') - if (_self.$input.data( 'edit' )) { - _self.unedit(true) - } - } - }) - - // Listen to window resize - $(window).on('resize', $.proxy(this.update, this )) - - } - - , keydown: function (e) { - - if (!this.focused) return - - var _self = this - - switch(e.keyCode) { - case 8: // backspace - if (!this.$input.is(document.activeElement)) break - this.lastInputValue = this.$input.val() - break - - case 37: // left arrow - leftRight( this.textDirection === 'rtl' ? 'next': 'prev' ) - break - - case 38: // up arrow - upDown('prev') - break - - case 39: // right arrow - leftRight( this.textDirection === 'rtl' ? 'prev': 'next' ) - break - - case 40: // down arrow - upDown('next') - break - - case 65: // a (to handle ctrl + a) - if (this.$input.val().length > 0 || !(e.ctrlKey || e.metaKey)) break - this.activateAll() - e.preventDefault() - break - - case 9: // tab - case 13: // enter - - // We will handle creating tokens from autocomplete in autocomplete events - if (this.$input.data('ui-autocomplete') && this.$input.data('ui-autocomplete').menu.element.find("li:has(a.ui-state-focus), li.ui-state-focus").length) break - - // We will handle creating tokens from typeahead in typeahead events - if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-cursor').length ) break - if (this.$input.hasClass('tt-input') && this.$wrapper.find('.tt-hint').val() && this.$wrapper.find('.tt-hint').val().length) break - - // Create token - if (this.$input.is(document.activeElement) && this.$input.val().length || this.$input.data('edit')) { - return this.createTokensFromInput(e, this.$input.data('edit')); - } - - // Edit token - if (e.keyCode === 13) { - if (!this.$copyHelper.is(document.activeElement) || this.$wrapper.find('.token.active').length !== 1) break - if (!_self.options.allowEditing) break - this.edit( this.$wrapper.find('.token.active') ) - } - } - - function leftRight(direction) { - if (_self.$input.is(document.activeElement)) { - if (_self.$input.val().length > 0) return - - direction += 'All' - var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction]('.token:first') : _self.$input[direction]('.token:first') - if (!$token.length) return - - _self.preventInputFocus = true - _self.preventDeactivation = true - - _self.activate( $token ) - e.preventDefault() - - } else { - _self[direction]( e.shiftKey ) - e.preventDefault() - } - } - - function upDown(direction) { - if (!e.shiftKey) return - - if (_self.$input.is(document.activeElement)) { - if (_self.$input.val().length > 0) return - - var $token = _self.$input.hasClass('tt-input') ? _self.$input.parent()[direction + 'All']('.token:first') : _self.$input[direction + 'All']('.token:first') - if (!$token.length) return - - _self.activate( $token ) - } - - var opposite = direction === 'prev' ? 'next' : 'prev' - , position = direction === 'prev' ? 'first' : 'last' - - _self.$firstActiveToken[opposite + 'All']('.token').each(function() { - _self.deactivate( $(this) ) - }) - - _self.activate( _self.$wrapper.find('.token:' + position), true, true ) - e.preventDefault() - } - - this.lastKeyDown = e.keyCode - } - - , keypress: function(e) { - - // Comma - if ($.inArray( e.which, this._triggerKeys) !== -1 && this.$input.is(document.activeElement)) { - if (this.$input.val()) { - this.createTokensFromInput(e) - } - return false; - } - } - - , keyup: function (e) { - this.preventInputFocus = false - - if (!this.focused) return - - switch(e.keyCode) { - case 8: // backspace - if (this.$input.is(document.activeElement)) { - if (this.$input.val().length || this.lastInputValue.length && this.lastKeyDown === 8) break - - this.preventDeactivation = true - var $prevToken = this.$input.hasClass('tt-input') ? this.$input.parent().prevAll('.token:first') : this.$input.prevAll('.token:first') - - if (!$prevToken.length) break - - this.activate( $prevToken ) - } else { - this.remove(e) - } - break - - case 46: // delete - this.remove(e, 'next') - break - } - this.lastKeyUp = e.keyCode - } - - , focus: function (e) { - this.focused = true - this.$wrapper.addClass('focus') - - if (this.$input.is(document.activeElement)) { - this.$wrapper.find('.active').removeClass('active') - this.$firstActiveToken = null - - if (this.options.showAutocompleteOnFocus) { - this.search() - } - } - } - - , blur: function (e) { - - this.focused = false - this.$wrapper.removeClass('focus') - - if (!this.preventDeactivation && !this.$element.is(document.activeElement)) { - this.$wrapper.find('.active').removeClass('active') - this.$firstActiveToken = null - } - - if (!this.preventCreateTokens && (this.$input.data('edit') && !this.$input.is(document.activeElement) || this.options.createTokensOnBlur )) { - this.createTokensFromInput(e) - } - - this.preventDeactivation = false - this.preventCreateTokens = false - } - - , paste: function (e) { - var _self = this - - // Add tokens to existing ones - if (_self.options.allowPasting) { - setTimeout(function () { - _self.createTokensFromInput(e) - }, 1) - } - } - - , change: function (e) { - if ( e.initiator === 'tokenfield' ) return // Prevent loops - - this.setTokens( this.$element.val() ) - } - - , createTokensFromInput: function (e, focus) { - if (this.$input.val().length < this.options.minLength) - return // No input, simply return - - var tokensBefore = this.getTokensList() - this.setTokens( this.$input.val(), true ) - - if (tokensBefore == this.getTokensList() && this.$input.val().length) - return false // No tokens were added, do nothing (prevent form submit) - - this.setInput('') - - if (this.$input.data( 'edit' )) { - this.unedit(focus) - } - - return false // Prevent form being submitted - } - - , next: function (add) { - if (add) { - var $firstActiveToken = this.$wrapper.find('.active:first') - , deactivate = $firstActiveToken && this.$firstActiveToken ? $firstActiveToken.index() < this.$firstActiveToken.index() : false - - if (deactivate) return this.deactivate( $firstActiveToken ) - } - - var $lastActiveToken = this.$wrapper.find('.active:last') - , $nextToken = $lastActiveToken.nextAll('.token:first') - - if (!$nextToken.length) { - this.$input.focus() - return - } - - this.activate($nextToken, add) - } - - , prev: function (add) { - - if (add) { - var $lastActiveToken = this.$wrapper.find('.active:last') - , deactivate = $lastActiveToken && this.$firstActiveToken ? $lastActiveToken.index() > this.$firstActiveToken.index() : false - - if (deactivate) return this.deactivate( $lastActiveToken ) - } - - var $firstActiveToken = this.$wrapper.find('.active:first') - , $prevToken = $firstActiveToken.prevAll('.token:first') - - if (!$prevToken.length) { - $prevToken = this.$wrapper.find('.token:first') - } - - if (!$prevToken.length && !add) { - this.$input.focus() - return - } - - this.activate( $prevToken, add ) - } - - , activate: function ($token, add, multi, remember) { - - if (!$token) return - - if (typeof remember === 'undefined') var remember = true - - if (multi) var add = true - - this.$copyHelper.focus() - - if (!add) { - this.$wrapper.find('.active').removeClass('active') - if (remember) { - this.$firstActiveToken = $token - } else { - delete this.$firstActiveToken - } - } - - if (multi && this.$firstActiveToken) { - // Determine first active token and the current tokens indicies - // Account for the 1 hidden textarea by subtracting 1 from both - var i = this.$firstActiveToken.index() - 2 - , a = $token.index() - 2 - , _self = this - - this.$wrapper.find('.token').slice( Math.min(i, a) + 1, Math.max(i, a) ).each( function() { - _self.activate( $(this), true ) - }) - } - - $token.addClass('active') - this.$copyHelper.val( this.getTokensList( null, null, true ) ).select() - } - - , activateAll: function() { - var _self = this - - this.$wrapper.find('.token').each( function (i) { - _self.activate($(this), i !== 0, false, false) - }) - } - - , deactivate: function($token) { - if (!$token) return - - $token.removeClass('active') - this.$copyHelper.val( this.getTokensList( null, null, true ) ).select() - } - - , toggle: function($token) { - if (!$token) return - - $token.toggleClass('active') - this.$copyHelper.val( this.getTokensList( null, null, true ) ).select() - } - - , edit: function ($token) { - if (!$token) return - - var attrs = $token.data('attrs') - - // Allow changing input value before editing - var options = { attrs: attrs, relatedTarget: $token.get(0) } - var editEvent = $.Event('tokenfield:edittoken', options) - this.$element.trigger( editEvent ) - - // Edit event can be cancelled if default is prevented - if (editEvent.isDefaultPrevented()) return - - $token.find('.token-label').text(attrs.value) - var tokenWidth = $token.outerWidth() - - var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input - - $token.replaceWith( $_input ) - - this.preventCreateTokens = true - - this.$input.val( attrs.value ) - .select() - .data( 'edit', true ) - .width( tokenWidth ) - - this.update(); - - // Indicate that token is now being edited, and is replaced with an input field in the DOM - this.$element.trigger($.Event('tokenfield:editedtoken', options )) - } - - , unedit: function (focus) { - var $_input = this.$input.hasClass('tt-input') ? this.$input.parent() : this.$input - $_input.appendTo( this.$wrapper ) - - this.$input.data('edit', false) - this.$mirror.text('') - - this.update() - - // Because moving the input element around in DOM - // will cause it to lose focus, we provide an option - // to re-focus the input after appending it to the wrapper - if (focus) { - var _self = this - setTimeout(function () { - _self.$input.focus() - }, 1) - } - } - - , remove: function (e, direction) { - if (this.$input.is(document.activeElement) || this._disabled || this._readonly) return - - var $token = (e.type === 'click') ? $(e.target).closest('.token') : this.$wrapper.find('.token.active') - - if (e.type !== 'click') { - if (!direction) var direction = 'prev' - this[direction]() - - // Was it the first token? - if (direction === 'prev') var firstToken = $token.first().prevAll('.token:first').length === 0 - } - - // Prepare events and their options - var options = { attrs: this.getTokenData( $token ), relatedTarget: $token.get(0) } - , removeEvent = $.Event('tokenfield:removetoken', options) - - this.$element.trigger(removeEvent); - - // Remove event can be intercepted and cancelled - if (removeEvent.isDefaultPrevented()) return - - var removedEvent = $.Event('tokenfield:removedtoken', options) - , changeEvent = $.Event('change', { initiator: 'tokenfield' }) - - // Remove token from DOM - $token.remove() - - // Trigger events - this.$element.val( this.getTokensList() ).trigger( removedEvent ).trigger( changeEvent ) - - // Focus, when necessary: - // When there are no more tokens, or if this was the first token - // and it was removed with backspace or it was clicked on - if (!this.$wrapper.find('.token').length || e.type === 'click' || firstToken) this.$input.focus() - - // Adjust input width - this.$input.css('width', this.options.minWidth + 'px') - this.update() - - // Cancel original event handlers - e.preventDefault() - e.stopPropagation() - } - - /** - * Update tokenfield dimensions - */ - , update: function (e) { - var value = this.$input.val() - , inputPaddingLeft = parseInt(this.$input.css('padding-left'), 10) - , inputPaddingRight = parseInt(this.$input.css('padding-right'), 10) - , inputPadding = inputPaddingLeft + inputPaddingRight - - if (this.$input.data('edit')) { - - if (!value) { - value = this.$input.prop("placeholder") - } - if (value === this.$mirror.text()) return - - this.$mirror.text(value) - - var mirrorWidth = this.$mirror.width() + 10; - if ( mirrorWidth > this.$wrapper.width() ) { - return this.$input.width( this.$wrapper.width() ) - } - - this.$input.width( mirrorWidth ) - } - else { - //temporary reset width to minimal value to get proper results - this.$input.width(this.options.minWidth); - - var w = (this.textDirection === 'rtl') - ? this.$input.offset().left + this.$input.outerWidth() - this.$wrapper.offset().left - parseInt(this.$wrapper.css('padding-left'), 10) - inputPadding - 1 - : this.$wrapper.offset().left + this.$wrapper.width() + parseInt(this.$wrapper.css('padding-left'), 10) - this.$input.offset().left - inputPadding; - // - // some usecases pre-render widget before attaching to DOM, - // dimensions returned by jquery will be NaN -> we default to 100% - // so placeholder won't be cut off. - isNaN(w) ? this.$input.width('100%') : this.$input.width(w); - } - } - - , focusInput: function (e) { - if ( $(e.target).closest('.token').length || $(e.target).closest('.token-input').length || $(e.target).closest('.tt-dropdown-menu').length ) return - // Focus only after the current call stack has cleared, - // otherwise has no effect. - // Reason: mousedown is too early - input will lose focus - // after mousedown. However, since the input may be moved - // in DOM, there may be no click or mouseup event triggered. - var _self = this - setTimeout(function() { - _self.$input.focus() - }, 0) - } - - , search: function () { - if ( this.$input.data('ui-autocomplete') ) { - this.$input.autocomplete('search') - } - } - - , disable: function () { - this.setProperty('disabled', true); - } - - , enable: function () { - this.setProperty('disabled', false); - } - - , readonly: function () { - this.setProperty('readonly', true); - } - - , writeable: function () { - this.setProperty('readonly', false); - } - - , setProperty: function(property, value) { - this['_' + property] = value; - this.$input.prop(property, value); - this.$element.prop(property, value); - this.$wrapper[ value ? 'addClass' : 'removeClass' ](property); - } - - , destroy: function() { - // Set field value - this.$element.val( this.getTokensList() ); - // Restore styles and properties - this.$element.css( this.$element.data('original-styles') ); - this.$element.prop( 'tabindex', this.$element.data('original-tabindex') ); - - // Re-route tokenfield label to original input - var $label = $( 'label[for="' + this.$input.prop('id') + '"]' ) - if ( $label.length ) { - $label.prop( 'for', this.$element.prop('id') ) - } - - // Move original element outside of tokenfield wrapper - this.$element.insertBefore( this.$wrapper ); - - // Remove tokenfield-related data - this.$element.removeData('original-styles') - .removeData('original-tabindex') - .removeData('bs.tokenfield'); - - // Remove tokenfield from DOM - this.$wrapper.remove(); - this.$mirror.remove(); - - var $_element = this.$element; - - return $_element; - } - - } - - - /* TOKENFIELD PLUGIN DEFINITION - * ======================== */ - - var old = $.fn.tokenfield - - $.fn.tokenfield = function (option, param) { - var value - , args = [] - - Array.prototype.push.apply( args, arguments ); - - var elements = this.each(function () { - var $this = $(this) - , data = $this.data('bs.tokenfield') - , options = typeof option == 'object' && option - - if (typeof option === 'string' && data && data[option]) { - args.shift() - value = data[option].apply(data, args) - } else { - if (!data && typeof option !== 'string' && !param) { - $this.data('bs.tokenfield', (data = new Tokenfield(this, options))) - $this.trigger('tokenfield:initialize') - } - } - }) - - return typeof value !== 'undefined' ? value : elements; - } - - $.fn.tokenfield.defaults = { - minWidth: 60, - minLength: 0, - html: true, - allowEditing: true, - allowPasting: true, - limit: 0, - autocomplete: {}, - typeahead: {}, - showAutocompleteOnFocus: false, - createTokensOnBlur: false, - delimiter: ',', - beautify: true, - inputType: 'text' - } - - $.fn.tokenfield.Constructor = Tokenfield - - - /* TOKENFIELD NO CONFLICT - * ================== */ - - $.fn.tokenfield.noConflict = function () { - $.fn.tokenfield = old - return this - } - - return Tokenfield; - -})); diff --git a/static/js/typeahead/LICENSE b/static/js/typeahead/LICENSE deleted file mode 100644 index 83817bac..00000000 --- a/static/js/typeahead/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013-2014 Twitter, Inc - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/static/js/typeahead/typeahead.js b/static/js/typeahead/typeahead.js deleted file mode 100644 index 14f97d68..00000000 --- a/static/js/typeahead/typeahead.js +++ /dev/null @@ -1,2451 +0,0 @@ -/*! - * typeahead.js 0.11.1 - * https://github.com/twitter/typeahead.js - * Copyright 2013-2015 Twitter, Inc. and other contributors; Licensed MIT - */ - -(function(root, factory) { - if (typeof define === "function" && define.amd) { - define("bloodhound", [ "jquery" ], function(a0) { - return root["Bloodhound"] = factory(a0); - }); - } else if (typeof exports === "object") { - module.exports = factory(require("jquery")); - } else { - root["Bloodhound"] = factory(jQuery); - } -})(this, function($) { - var _ = function() { - "use strict"; - return { - isMsie: function() { - return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; - }, - isBlankString: function(str) { - return !str || /^\s*$/.test(str); - }, - escapeRegExChars: function(str) { - return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - }, - isString: function(obj) { - return typeof obj === "string"; - }, - isNumber: function(obj) { - return typeof obj === "number"; - }, - isArray: $.isArray, - isFunction: $.isFunction, - isObject: $.isPlainObject, - isUndefined: function(obj) { - return typeof obj === "undefined"; - }, - isElement: function(obj) { - return !!(obj && obj.nodeType === 1); - }, - isJQuery: function(obj) { - return obj instanceof $; - }, - toStr: function toStr(s) { - return _.isUndefined(s) || s === null ? "" : s + ""; - }, - bind: $.proxy, - each: function(collection, cb) { - $.each(collection, reverseArgs); - function reverseArgs(index, value) { - return cb(value, index); - } - }, - map: $.map, - filter: $.grep, - every: function(obj, test) { - var result = true; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (!(result = test.call(null, val, key, obj))) { - return false; - } - }); - return !!result; - }, - some: function(obj, test) { - var result = false; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (result = test.call(null, val, key, obj)) { - return false; - } - }); - return !!result; - }, - mixin: $.extend, - identity: function(x) { - return x; - }, - clone: function(obj) { - return $.extend(true, {}, obj); - }, - getIdGenerator: function() { - var counter = 0; - return function() { - return counter++; - }; - }, - templatify: function templatify(obj) { - return $.isFunction(obj) ? obj : template; - function template() { - return String(obj); - } - }, - defer: function(fn) { - setTimeout(fn, 0); - }, - debounce: function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments, later, callNow; - later = function() { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - } - }; - callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) { - result = func.apply(context, args); - } - return result; - }; - }, - throttle: function(func, wait) { - var context, args, timeout, result, previous, later; - previous = 0; - later = function() { - previous = new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date(), remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }, - stringify: function(val) { - return _.isString(val) ? val : JSON.stringify(val); - }, - noop: function() {} - }; - }(); - var VERSION = "0.11.1"; - var tokenizers = function() { - "use strict"; - return { - nonword: nonword, - whitespace: whitespace, - obj: { - nonword: getObjTokenizer(nonword), - whitespace: getObjTokenizer(whitespace) - } - }; - function whitespace(str) { - str = _.toStr(str); - return str ? str.split(/\s+/) : []; - } - function nonword(str) { - str = _.toStr(str); - return str ? str.split(/\W+/) : []; - } - function getObjTokenizer(tokenizer) { - return function setKey(keys) { - keys = _.isArray(keys) ? keys : [].slice.call(arguments, 0); - return function tokenize(o) { - var tokens = []; - _.each(keys, function(k) { - tokens = tokens.concat(tokenizer(_.toStr(o[k]))); - }); - return tokens; - }; - }; - } - }(); - var LruCache = function() { - "use strict"; - function LruCache(maxSize) { - this.maxSize = _.isNumber(maxSize) ? maxSize : 100; - this.reset(); - if (this.maxSize <= 0) { - this.set = this.get = $.noop; - } - } - _.mixin(LruCache.prototype, { - set: function set(key, val) { - var tailItem = this.list.tail, node; - if (this.size >= this.maxSize) { - this.list.remove(tailItem); - delete this.hash[tailItem.key]; - this.size--; - } - if (node = this.hash[key]) { - node.val = val; - this.list.moveToFront(node); - } else { - node = new Node(key, val); - this.list.add(node); - this.hash[key] = node; - this.size++; - } - }, - get: function get(key) { - var node = this.hash[key]; - if (node) { - this.list.moveToFront(node); - return node.val; - } - }, - reset: function reset() { - this.size = 0; - this.hash = {}; - this.list = new List(); - } - }); - function List() { - this.head = this.tail = null; - } - _.mixin(List.prototype, { - add: function add(node) { - if (this.head) { - node.next = this.head; - this.head.prev = node; - } - this.head = node; - this.tail = this.tail || node; - }, - remove: function remove(node) { - node.prev ? node.prev.next = node.next : this.head = node.next; - node.next ? node.next.prev = node.prev : this.tail = node.prev; - }, - moveToFront: function(node) { - this.remove(node); - this.add(node); - } - }); - function Node(key, val) { - this.key = key; - this.val = val; - this.prev = this.next = null; - } - return LruCache; - }(); - var PersistentStorage = function() { - "use strict"; - var LOCAL_STORAGE; - try { - LOCAL_STORAGE = window.localStorage; - LOCAL_STORAGE.setItem("~~~", "!"); - LOCAL_STORAGE.removeItem("~~~"); - } catch (err) { - LOCAL_STORAGE = null; - } - function PersistentStorage(namespace, override) { - this.prefix = [ "__", namespace, "__" ].join(""); - this.ttlKey = "__ttl__"; - this.keyMatcher = new RegExp("^" + _.escapeRegExChars(this.prefix)); - this.ls = override || LOCAL_STORAGE; - !this.ls && this._noop(); - } - _.mixin(PersistentStorage.prototype, { - _prefix: function(key) { - return this.prefix + key; - }, - _ttlKey: function(key) { - return this._prefix(key) + this.ttlKey; - }, - _noop: function() { - this.get = this.set = this.remove = this.clear = this.isExpired = _.noop; - }, - _safeSet: function(key, val) { - try { - this.ls.setItem(key, val); - } catch (err) { - if (err.name === "QuotaExceededError") { - this.clear(); - this._noop(); - } - } - }, - get: function(key) { - if (this.isExpired(key)) { - this.remove(key); - } - return decode(this.ls.getItem(this._prefix(key))); - }, - set: function(key, val, ttl) { - if (_.isNumber(ttl)) { - this._safeSet(this._ttlKey(key), encode(now() + ttl)); - } else { - this.ls.removeItem(this._ttlKey(key)); - } - return this._safeSet(this._prefix(key), encode(val)); - }, - remove: function(key) { - this.ls.removeItem(this._ttlKey(key)); - this.ls.removeItem(this._prefix(key)); - return this; - }, - clear: function() { - var i, keys = gatherMatchingKeys(this.keyMatcher); - for (i = keys.length; i--; ) { - this.remove(keys[i]); - } - return this; - }, - isExpired: function(key) { - var ttl = decode(this.ls.getItem(this._ttlKey(key))); - return _.isNumber(ttl) && now() > ttl ? true : false; - } - }); - return PersistentStorage; - function now() { - return new Date().getTime(); - } - function encode(val) { - return JSON.stringify(_.isUndefined(val) ? null : val); - } - function decode(val) { - return $.parseJSON(val); - } - function gatherMatchingKeys(keyMatcher) { - var i, key, keys = [], len = LOCAL_STORAGE.length; - for (i = 0; i < len; i++) { - if ((key = LOCAL_STORAGE.key(i)).match(keyMatcher)) { - keys.push(key.replace(keyMatcher, "")); - } - } - return keys; - } - }(); - var Transport = function() { - "use strict"; - var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests = 6, sharedCache = new LruCache(10); - function Transport(o) { - o = o || {}; - this.cancelled = false; - this.lastReq = null; - this._send = o.transport; - this._get = o.limiter ? o.limiter(this._get) : this._get; - this._cache = o.cache === false ? new LruCache(0) : sharedCache; - } - Transport.setMaxPendingRequests = function setMaxPendingRequests(num) { - maxPendingRequests = num; - }; - Transport.resetCache = function resetCache() { - sharedCache.reset(); - }; - _.mixin(Transport.prototype, { - _fingerprint: function fingerprint(o) { - o = o || {}; - return o.url + o.type + $.param(o.data || {}); - }, - _get: function(o, cb) { - var that = this, fingerprint, jqXhr; - fingerprint = this._fingerprint(o); - if (this.cancelled || fingerprint !== this.lastReq) { - return; - } - if (jqXhr = pendingRequests[fingerprint]) { - jqXhr.done(done).fail(fail); - } else if (pendingRequestsCount < maxPendingRequests) { - pendingRequestsCount++; - pendingRequests[fingerprint] = this._send(o).done(done).fail(fail).always(always); - } else { - this.onDeckRequestArgs = [].slice.call(arguments, 0); - } - function done(resp) { - cb(null, resp); - that._cache.set(fingerprint, resp); - } - function fail() { - cb(true); - } - function always() { - pendingRequestsCount--; - delete pendingRequests[fingerprint]; - if (that.onDeckRequestArgs) { - that._get.apply(that, that.onDeckRequestArgs); - that.onDeckRequestArgs = null; - } - } - }, - get: function(o, cb) { - var resp, fingerprint; - cb = cb || $.noop; - o = _.isString(o) ? { - url: o - } : o || {}; - fingerprint = this._fingerprint(o); - this.cancelled = false; - this.lastReq = fingerprint; - if (resp = this._cache.get(fingerprint)) { - cb(null, resp); - } else { - this._get(o, cb); - } - }, - cancel: function() { - this.cancelled = true; - } - }); - return Transport; - }(); - var SearchIndex = window.SearchIndex = function() { - "use strict"; - var CHILDREN = "c", IDS = "i"; - function SearchIndex(o) { - o = o || {}; - if (!o.datumTokenizer || !o.queryTokenizer) { - $.error("datumTokenizer and queryTokenizer are both required"); - } - this.identify = o.identify || _.stringify; - this.datumTokenizer = o.datumTokenizer; - this.queryTokenizer = o.queryTokenizer; - this.reset(); - } - _.mixin(SearchIndex.prototype, { - bootstrap: function bootstrap(o) { - this.datums = o.datums; - this.trie = o.trie; - }, - add: function(data) { - var that = this; - data = _.isArray(data) ? data : [ data ]; - _.each(data, function(datum) { - var id, tokens; - that.datums[id = that.identify(datum)] = datum; - tokens = normalizeTokens(that.datumTokenizer(datum)); - _.each(tokens, function(token) { - var node, chars, ch; - node = that.trie; - chars = token.split(""); - while (ch = chars.shift()) { - node = node[CHILDREN][ch] || (node[CHILDREN][ch] = newNode()); - node[IDS].push(id); - } - }); - }); - }, - get: function get(ids) { - var that = this; - return _.map(ids, function(id) { - return that.datums[id]; - }); - }, - search: function search(query) { - var that = this, tokens, matches; - tokens = normalizeTokens(this.queryTokenizer(query)); - _.each(tokens, function(token) { - var node, chars, ch, ids; - if (matches && matches.length === 0) { - return false; - } - node = that.trie; - chars = token.split(""); - while (node && (ch = chars.shift())) { - node = node[CHILDREN][ch]; - } - if (node && chars.length === 0) { - ids = node[IDS].slice(0); - matches = matches ? getIntersection(matches, ids) : ids; - } else { - matches = []; - return false; - } - }); - return matches ? _.map(unique(matches), function(id) { - return that.datums[id]; - }) : []; - }, - all: function all() { - var values = []; - for (var key in this.datums) { - values.push(this.datums[key]); - } - return values; - }, - reset: function reset() { - this.datums = {}; - this.trie = newNode(); - }, - serialize: function serialize() { - return { - datums: this.datums, - trie: this.trie - }; - } - }); - return SearchIndex; - function normalizeTokens(tokens) { - tokens = _.filter(tokens, function(token) { - return !!token; - }); - tokens = _.map(tokens, function(token) { - return token.toLowerCase(); - }); - return tokens; - } - function newNode() { - var node = {}; - node[IDS] = []; - node[CHILDREN] = {}; - return node; - } - function unique(array) { - var seen = {}, uniques = []; - for (var i = 0, len = array.length; i < len; i++) { - if (!seen[array[i]]) { - seen[array[i]] = true; - uniques.push(array[i]); - } - } - return uniques; - } - function getIntersection(arrayA, arrayB) { - var ai = 0, bi = 0, intersection = []; - arrayA = arrayA.sort(); - arrayB = arrayB.sort(); - var lenArrayA = arrayA.length, lenArrayB = arrayB.length; - while (ai < lenArrayA && bi < lenArrayB) { - if (arrayA[ai] < arrayB[bi]) { - ai++; - } else if (arrayA[ai] > arrayB[bi]) { - bi++; - } else { - intersection.push(arrayA[ai]); - ai++; - bi++; - } - } - return intersection; - } - }(); - var Prefetch = function() { - "use strict"; - var keys; - keys = { - data: "data", - protocol: "protocol", - thumbprint: "thumbprint" - }; - function Prefetch(o) { - this.url = o.url; - this.ttl = o.ttl; - this.cache = o.cache; - this.prepare = o.prepare; - this.transform = o.transform; - this.transport = o.transport; - this.thumbprint = o.thumbprint; - this.storage = new PersistentStorage(o.cacheKey); - } - _.mixin(Prefetch.prototype, { - _settings: function settings() { - return { - url: this.url, - type: "GET", - dataType: "json" - }; - }, - store: function store(data) { - if (!this.cache) { - return; - } - this.storage.set(keys.data, data, this.ttl); - this.storage.set(keys.protocol, location.protocol, this.ttl); - this.storage.set(keys.thumbprint, this.thumbprint, this.ttl); - }, - fromCache: function fromCache() { - var stored = {}, isExpired; - if (!this.cache) { - return null; - } - stored.data = this.storage.get(keys.data); - stored.protocol = this.storage.get(keys.protocol); - stored.thumbprint = this.storage.get(keys.thumbprint); - isExpired = stored.thumbprint !== this.thumbprint || stored.protocol !== location.protocol; - return stored.data && !isExpired ? stored.data : null; - }, - fromNetwork: function(cb) { - var that = this, settings; - if (!cb) { - return; - } - settings = this.prepare(this._settings()); - this.transport(settings).fail(onError).done(onResponse); - function onError() { - cb(true); - } - function onResponse(resp) { - cb(null, that.transform(resp)); - } - }, - clear: function clear() { - this.storage.clear(); - return this; - } - }); - return Prefetch; - }(); - var Remote = function() { - "use strict"; - function Remote(o) { - this.url = o.url; - this.prepare = o.prepare; - this.transform = o.transform; - this.transport = new Transport({ - cache: o.cache, - limiter: o.limiter, - transport: o.transport - }); - } - _.mixin(Remote.prototype, { - _settings: function settings() { - return { - url: this.url, - type: "GET", - dataType: "json" - }; - }, - get: function get(query, cb) { - var that = this, settings; - if (!cb) { - return; - } - query = query || ""; - settings = this.prepare(query, this._settings()); - return this.transport.get(settings, onResponse); - function onResponse(err, resp) { - err ? cb([]) : cb(that.transform(resp)); - } - }, - cancelLastRequest: function cancelLastRequest() { - this.transport.cancel(); - } - }); - return Remote; - }(); - var oParser = function() { - "use strict"; - return function parse(o) { - var defaults, sorter; - defaults = { - initialize: true, - identify: _.stringify, - datumTokenizer: null, - queryTokenizer: null, - sufficient: 5, - sorter: null, - local: [], - prefetch: null, - remote: null - }; - o = _.mixin(defaults, o || {}); - !o.datumTokenizer && $.error("datumTokenizer is required"); - !o.queryTokenizer && $.error("queryTokenizer is required"); - sorter = o.sorter; - o.sorter = sorter ? function(x) { - return x.sort(sorter); - } : _.identity; - o.local = _.isFunction(o.local) ? o.local() : o.local; - o.prefetch = parsePrefetch(o.prefetch); - o.remote = parseRemote(o.remote); - return o; - }; - function parsePrefetch(o) { - var defaults; - if (!o) { - return null; - } - defaults = { - url: null, - ttl: 24 * 60 * 60 * 1e3, - cache: true, - cacheKey: null, - thumbprint: "", - prepare: _.identity, - transform: _.identity, - transport: null - }; - o = _.isString(o) ? { - url: o - } : o; - o = _.mixin(defaults, o); - !o.url && $.error("prefetch requires url to be set"); - o.transform = o.filter || o.transform; - o.cacheKey = o.cacheKey || o.url; - o.thumbprint = VERSION + o.thumbprint; - o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax; - return o; - } - function parseRemote(o) { - var defaults; - if (!o) { - return; - } - defaults = { - url: null, - cache: true, - prepare: null, - replace: null, - wildcard: null, - limiter: null, - rateLimitBy: "debounce", - rateLimitWait: 300, - transform: _.identity, - transport: null - }; - o = _.isString(o) ? { - url: o - } : o; - o = _.mixin(defaults, o); - !o.url && $.error("remote requires url to be set"); - o.transform = o.filter || o.transform; - o.prepare = toRemotePrepare(o); - o.limiter = toLimiter(o); - o.transport = o.transport ? callbackToDeferred(o.transport) : $.ajax; - delete o.replace; - delete o.wildcard; - delete o.rateLimitBy; - delete o.rateLimitWait; - return o; - } - function toRemotePrepare(o) { - var prepare, replace, wildcard; - prepare = o.prepare; - replace = o.replace; - wildcard = o.wildcard; - if (prepare) { - return prepare; - } - if (replace) { - prepare = prepareByReplace; - } else if (o.wildcard) { - prepare = prepareByWildcard; - } else { - prepare = idenityPrepare; - } - return prepare; - function prepareByReplace(query, settings) { - settings.url = replace(settings.url, query); - return settings; - } - function prepareByWildcard(query, settings) { - settings.url = settings.url.replace(wildcard, encodeURIComponent(query)); - return settings; - } - function idenityPrepare(query, settings) { - return settings; - } - } - function toLimiter(o) { - var limiter, method, wait; - limiter = o.limiter; - method = o.rateLimitBy; - wait = o.rateLimitWait; - if (!limiter) { - limiter = /^throttle$/i.test(method) ? throttle(wait) : debounce(wait); - } - return limiter; - function debounce(wait) { - return function debounce(fn) { - return _.debounce(fn, wait); - }; - } - function throttle(wait) { - return function throttle(fn) { - return _.throttle(fn, wait); - }; - } - } - function callbackToDeferred(fn) { - return function wrapper(o) { - var deferred = $.Deferred(); - fn(o, onSuccess, onError); - return deferred; - function onSuccess(resp) { - _.defer(function() { - deferred.resolve(resp); - }); - } - function onError(err) { - _.defer(function() { - deferred.reject(err); - }); - } - }; - } - }(); - var Bloodhound = function() { - "use strict"; - var old; - old = window && window.Bloodhound; - function Bloodhound(o) { - o = oParser(o); - this.sorter = o.sorter; - this.identify = o.identify; - this.sufficient = o.sufficient; - this.local = o.local; - this.remote = o.remote ? new Remote(o.remote) : null; - this.prefetch = o.prefetch ? new Prefetch(o.prefetch) : null; - this.index = new SearchIndex({ - identify: this.identify, - datumTokenizer: o.datumTokenizer, - queryTokenizer: o.queryTokenizer - }); - o.initialize !== false && this.initialize(); - } - Bloodhound.noConflict = function noConflict() { - window && (window.Bloodhound = old); - return Bloodhound; - }; - Bloodhound.tokenizers = tokenizers; - _.mixin(Bloodhound.prototype, { - __ttAdapter: function ttAdapter() { - var that = this; - return this.remote ? withAsync : withoutAsync; - function withAsync(query, sync, async) { - return that.search(query, sync, async); - } - function withoutAsync(query, sync) { - return that.search(query, sync); - } - }, - _loadPrefetch: function loadPrefetch() { - var that = this, deferred, serialized; - deferred = $.Deferred(); - if (!this.prefetch) { - deferred.resolve(); - } else if (serialized = this.prefetch.fromCache()) { - this.index.bootstrap(serialized); - deferred.resolve(); - } else { - this.prefetch.fromNetwork(done); - } - return deferred.promise(); - function done(err, data) { - if (err) { - return deferred.reject(); - } - that.add(data); - that.prefetch.store(that.index.serialize()); - deferred.resolve(); - } - }, - _initialize: function initialize() { - var that = this, deferred; - this.clear(); - (this.initPromise = this._loadPrefetch()).done(addLocalToIndex); - return this.initPromise; - function addLocalToIndex() { - that.add(that.local); - } - }, - initialize: function initialize(force) { - return !this.initPromise || force ? this._initialize() : this.initPromise; - }, - add: function add(data) { - this.index.add(data); - return this; - }, - get: function get(ids) { - ids = _.isArray(ids) ? ids : [].slice.call(arguments); - return this.index.get(ids); - }, - search: function search(query, sync, async) { - var that = this, local; - local = this.sorter(this.index.search(query)); - sync(this.remote ? local.slice() : local); - if (this.remote && local.length < this.sufficient) { - this.remote.get(query, processRemote); - } else if (this.remote) { - this.remote.cancelLastRequest(); - } - return this; - function processRemote(remote) { - var nonDuplicates = []; - _.each(remote, function(r) { - !_.some(local, function(l) { - return that.identify(r) === that.identify(l); - }) && nonDuplicates.push(r); - }); - async && async(nonDuplicates); - } - }, - all: function all() { - return this.index.all(); - }, - clear: function clear() { - this.index.reset(); - return this; - }, - clearPrefetchCache: function clearPrefetchCache() { - this.prefetch && this.prefetch.clear(); - return this; - }, - clearRemoteCache: function clearRemoteCache() { - Transport.resetCache(); - return this; - }, - ttAdapter: function ttAdapter() { - return this.__ttAdapter(); - } - }); - return Bloodhound; - }(); - return Bloodhound; -}); - -(function(root, factory) { - if (typeof define === "function" && define.amd) { - define("typeahead.js", [ "jquery" ], function(a0) { - return factory(a0); - }); - } else if (typeof exports === "object") { - module.exports = factory(require("jquery")); - } else { - factory(jQuery); - } -})(this, function($) { - var _ = function() { - "use strict"; - return { - isMsie: function() { - return /(msie|trident)/i.test(navigator.userAgent) ? navigator.userAgent.match(/(msie |rv:)(\d+(.\d+)?)/i)[2] : false; - }, - isBlankString: function(str) { - return !str || /^\s*$/.test(str); - }, - escapeRegExChars: function(str) { - return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); - }, - isString: function(obj) { - return typeof obj === "string"; - }, - isNumber: function(obj) { - return typeof obj === "number"; - }, - isArray: $.isArray, - isFunction: $.isFunction, - isObject: $.isPlainObject, - isUndefined: function(obj) { - return typeof obj === "undefined"; - }, - isElement: function(obj) { - return !!(obj && obj.nodeType === 1); - }, - isJQuery: function(obj) { - return obj instanceof $; - }, - toStr: function toStr(s) { - return _.isUndefined(s) || s === null ? "" : s + ""; - }, - bind: $.proxy, - each: function(collection, cb) { - $.each(collection, reverseArgs); - function reverseArgs(index, value) { - return cb(value, index); - } - }, - map: $.map, - filter: $.grep, - every: function(obj, test) { - var result = true; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (!(result = test.call(null, val, key, obj))) { - return false; - } - }); - return !!result; - }, - some: function(obj, test) { - var result = false; - if (!obj) { - return result; - } - $.each(obj, function(key, val) { - if (result = test.call(null, val, key, obj)) { - return false; - } - }); - return !!result; - }, - mixin: $.extend, - identity: function(x) { - return x; - }, - clone: function(obj) { - return $.extend(true, {}, obj); - }, - getIdGenerator: function() { - var counter = 0; - return function() { - return counter++; - }; - }, - templatify: function templatify(obj) { - return $.isFunction(obj) ? obj : template; - function template() { - return String(obj); - } - }, - defer: function(fn) { - setTimeout(fn, 0); - }, - debounce: function(func, wait, immediate) { - var timeout, result; - return function() { - var context = this, args = arguments, later, callNow; - later = function() { - timeout = null; - if (!immediate) { - result = func.apply(context, args); - } - }; - callNow = immediate && !timeout; - clearTimeout(timeout); - timeout = setTimeout(later, wait); - if (callNow) { - result = func.apply(context, args); - } - return result; - }; - }, - throttle: function(func, wait) { - var context, args, timeout, result, previous, later; - previous = 0; - later = function() { - previous = new Date(); - timeout = null; - result = func.apply(context, args); - }; - return function() { - var now = new Date(), remaining = wait - (now - previous); - context = this; - args = arguments; - if (remaining <= 0) { - clearTimeout(timeout); - timeout = null; - previous = now; - result = func.apply(context, args); - } else if (!timeout) { - timeout = setTimeout(later, remaining); - } - return result; - }; - }, - stringify: function(val) { - return _.isString(val) ? val : JSON.stringify(val); - }, - noop: function() {} - }; - }(); - var WWW = function() { - "use strict"; - var defaultClassNames = { - wrapper: "twitter-typeahead", - input: "tt-input", - hint: "tt-hint", - menu: "tt-menu", - dataset: "tt-dataset", - suggestion: "tt-suggestion", - selectable: "tt-selectable", - empty: "tt-empty", - open: "tt-open", - cursor: "tt-cursor", - highlight: "tt-highlight" - }; - return build; - function build(o) { - var www, classes; - classes = _.mixin({}, defaultClassNames, o); - www = { - css: buildCss(), - classes: classes, - html: buildHtml(classes), - selectors: buildSelectors(classes) - }; - return { - css: www.css, - html: www.html, - classes: www.classes, - selectors: www.selectors, - mixin: function(o) { - _.mixin(o, www); - } - }; - } - function buildHtml(c) { - return { - wrapper: '', - menu: '
    ' - }; - } - function buildSelectors(classes) { - var selectors = {}; - _.each(classes, function(v, k) { - selectors[k] = "." + v; - }); - return selectors; - } - function buildCss() { - var css = { - wrapper: { - position: "relative", - display: "inline-block" - }, - hint: { - position: "absolute", - top: "0", - left: "0", - borderColor: "transparent", - boxShadow: "none", - opacity: "1" - }, - input: { - position: "relative", - verticalAlign: "top", - backgroundColor: "transparent" - }, - inputWithNoHint: { - position: "relative", - verticalAlign: "top" - }, - menu: { - position: "absolute", - top: "100%", - left: "0", - zIndex: "100", - display: "none" - }, - ltr: { - left: "0", - right: "auto" - }, - rtl: { - left: "auto", - right: " 0" - } - }; - if (_.isMsie()) { - _.mixin(css.input, { - backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" - }); - } - return css; - } - }(); - var EventBus = function() { - "use strict"; - var namespace, deprecationMap; - namespace = "typeahead:"; - deprecationMap = { - render: "rendered", - cursorchange: "cursorchanged", - select: "selected", - autocomplete: "autocompleted" - }; - function EventBus(o) { - if (!o || !o.el) { - $.error("EventBus initialized without el"); - } - this.$el = $(o.el); - } - _.mixin(EventBus.prototype, { - _trigger: function(type, args) { - var $e; - $e = $.Event(namespace + type); - (args = args || []).unshift($e); - this.$el.trigger.apply(this.$el, args); - return $e; - }, - before: function(type) { - var args, $e; - args = [].slice.call(arguments, 1); - $e = this._trigger("before" + type, args); - return $e.isDefaultPrevented(); - }, - trigger: function(type) { - var deprecatedType; - this._trigger(type, [].slice.call(arguments, 1)); - if (deprecatedType = deprecationMap[type]) { - this._trigger(deprecatedType, [].slice.call(arguments, 1)); - } - } - }); - return EventBus; - }(); - var EventEmitter = function() { - "use strict"; - var splitter = /\s+/, nextTick = getNextTick(); - return { - onSync: onSync, - onAsync: onAsync, - off: off, - trigger: trigger - }; - function on(method, types, cb, context) { - var type; - if (!cb) { - return this; - } - types = types.split(splitter); - cb = context ? bindContext(cb, context) : cb; - this._callbacks = this._callbacks || {}; - while (type = types.shift()) { - this._callbacks[type] = this._callbacks[type] || { - sync: [], - async: [] - }; - this._callbacks[type][method].push(cb); - } - return this; - } - function onAsync(types, cb, context) { - return on.call(this, "async", types, cb, context); - } - function onSync(types, cb, context) { - return on.call(this, "sync", types, cb, context); - } - function off(types) { - var type; - if (!this._callbacks) { - return this; - } - types = types.split(splitter); - while (type = types.shift()) { - delete this._callbacks[type]; - } - return this; - } - function trigger(types) { - var type, callbacks, args, syncFlush, asyncFlush; - if (!this._callbacks) { - return this; - } - types = types.split(splitter); - args = [].slice.call(arguments, 1); - while ((type = types.shift()) && (callbacks = this._callbacks[type])) { - syncFlush = getFlush(callbacks.sync, this, [ type ].concat(args)); - asyncFlush = getFlush(callbacks.async, this, [ type ].concat(args)); - syncFlush() && nextTick(asyncFlush); - } - return this; - } - function getFlush(callbacks, context, args) { - return flush; - function flush() { - var cancelled; - for (var i = 0, len = callbacks.length; !cancelled && i < len; i += 1) { - cancelled = callbacks[i].apply(context, args) === false; - } - return !cancelled; - } - } - function getNextTick() { - var nextTickFn; - if (window.setImmediate) { - nextTickFn = function nextTickSetImmediate(fn) { - setImmediate(function() { - fn(); - }); - }; - } else { - nextTickFn = function nextTickSetTimeout(fn) { - setTimeout(function() { - fn(); - }, 0); - }; - } - return nextTickFn; - } - function bindContext(fn, context) { - return fn.bind ? fn.bind(context) : function() { - fn.apply(context, [].slice.call(arguments, 0)); - }; - } - }(); - var highlight = function(doc) { - "use strict"; - var defaults = { - node: null, - pattern: null, - tagName: "strong", - className: null, - wordsOnly: false, - caseSensitive: false - }; - return function hightlight(o) { - var regex; - o = _.mixin({}, defaults, o); - if (!o.node || !o.pattern) { - return; - } - o.pattern = _.isArray(o.pattern) ? o.pattern : [ o.pattern ]; - regex = getRegex(o.pattern, o.caseSensitive, o.wordsOnly); - traverse(o.node, hightlightTextNode); - function hightlightTextNode(textNode) { - var match, patternNode, wrapperNode; - if (match = regex.exec(textNode.data)) { - wrapperNode = doc.createElement(o.tagName); - o.className && (wrapperNode.className = o.className); - patternNode = textNode.splitText(match.index); - patternNode.splitText(match[0].length); - wrapperNode.appendChild(patternNode.cloneNode(true)); - textNode.parentNode.replaceChild(wrapperNode, patternNode); - } - return !!match; - } - function traverse(el, hightlightTextNode) { - var childNode, TEXT_NODE_TYPE = 3; - for (var i = 0; i < el.childNodes.length; i++) { - childNode = el.childNodes[i]; - if (childNode.nodeType === TEXT_NODE_TYPE) { - i += hightlightTextNode(childNode) ? 1 : 0; - } else { - traverse(childNode, hightlightTextNode); - } - } - } - }; - function getRegex(patterns, caseSensitive, wordsOnly) { - var escapedPatterns = [], regexStr; - for (var i = 0, len = patterns.length; i < len; i++) { - escapedPatterns.push(_.escapeRegExChars(patterns[i])); - } - regexStr = wordsOnly ? "\\b(" + escapedPatterns.join("|") + ")\\b" : "(" + escapedPatterns.join("|") + ")"; - return caseSensitive ? new RegExp(regexStr) : new RegExp(regexStr, "i"); - } - }(window.document); - var Input = function() { - "use strict"; - var specialKeyCodeMap; - specialKeyCodeMap = { - 9: "tab", - 27: "esc", - 37: "left", - 39: "right", - 13: "enter", - 38: "up", - 40: "down" - }; - function Input(o, www) { - o = o || {}; - if (!o.input) { - $.error("input is missing"); - } - www.mixin(this); - this.$hint = $(o.hint); - this.$input = $(o.input); - this.query = this.$input.val(); - this.queryWhenFocused = this.hasFocus() ? this.query : null; - this.$overflowHelper = buildOverflowHelper(this.$input); - this._checkLanguageDirection(); - if (this.$hint.length === 0) { - this.setHint = this.getHint = this.clearHint = this.clearHintIfInvalid = _.noop; - } - } - Input.normalizeQuery = function(str) { - return _.toStr(str).replace(/^\s*/g, "").replace(/\s{2,}/g, " "); - }; - _.mixin(Input.prototype, EventEmitter, { - _onBlur: function onBlur() { - this.resetInputValue(); - this.trigger("blurred"); - }, - _onFocus: function onFocus() { - this.queryWhenFocused = this.query; - this.trigger("focused"); - }, - _onKeydown: function onKeydown($e) { - var keyName = specialKeyCodeMap[$e.which || $e.keyCode]; - this._managePreventDefault(keyName, $e); - if (keyName && this._shouldTrigger(keyName, $e)) { - this.trigger(keyName + "Keyed", $e); - } - }, - _onInput: function onInput() { - this._setQuery(this.getInputValue()); - this.clearHintIfInvalid(); - this._checkLanguageDirection(); - }, - _managePreventDefault: function managePreventDefault(keyName, $e) { - var preventDefault; - switch (keyName) { - case "up": - case "down": - preventDefault = !withModifier($e); - break; - - default: - preventDefault = false; - } - preventDefault && $e.preventDefault(); - }, - _shouldTrigger: function shouldTrigger(keyName, $e) { - var trigger; - switch (keyName) { - case "tab": - trigger = !withModifier($e); - break; - - default: - trigger = true; - } - return trigger; - }, - _checkLanguageDirection: function checkLanguageDirection() { - var dir = (this.$input.css("direction") || "ltr").toLowerCase(); - if (this.dir !== dir) { - this.dir = dir; - this.$hint.attr("dir", dir); - this.trigger("langDirChanged", dir); - } - }, - _setQuery: function setQuery(val, silent) { - var areEquivalent, hasDifferentWhitespace; - areEquivalent = areQueriesEquivalent(val, this.query); - hasDifferentWhitespace = areEquivalent ? this.query.length !== val.length : false; - this.query = val; - if (!silent && !areEquivalent) { - this.trigger("queryChanged", this.query); - } else if (!silent && hasDifferentWhitespace) { - this.trigger("whitespaceChanged", this.query); - } - }, - bind: function() { - var that = this, onBlur, onFocus, onKeydown, onInput; - onBlur = _.bind(this._onBlur, this); - onFocus = _.bind(this._onFocus, this); - onKeydown = _.bind(this._onKeydown, this); - onInput = _.bind(this._onInput, this); - this.$input.on("blur.tt", onBlur).on("focus.tt", onFocus).on("keydown.tt", onKeydown); - if (!_.isMsie() || _.isMsie() > 9) { - this.$input.on("input.tt", onInput); - } else { - this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { - if (specialKeyCodeMap[$e.which || $e.keyCode]) { - return; - } - _.defer(_.bind(that._onInput, that, $e)); - }); - } - return this; - }, - focus: function focus() { - this.$input.focus(); - }, - blur: function blur() { - this.$input.blur(); - }, - getLangDir: function getLangDir() { - return this.dir; - }, - getQuery: function getQuery() { - return this.query || ""; - }, - setQuery: function setQuery(val, silent) { - this.setInputValue(val); - this._setQuery(val, silent); - }, - hasQueryChangedSinceLastFocus: function hasQueryChangedSinceLastFocus() { - return this.query !== this.queryWhenFocused; - }, - getInputValue: function getInputValue() { - return this.$input.val(); - }, - setInputValue: function setInputValue(value) { - this.$input.val(value); - this.clearHintIfInvalid(); - this._checkLanguageDirection(); - }, - resetInputValue: function resetInputValue() { - this.setInputValue(this.query); - }, - getHint: function getHint() { - return this.$hint.val(); - }, - setHint: function setHint(value) { - this.$hint.val(value); - }, - clearHint: function clearHint() { - this.setHint(""); - }, - clearHintIfInvalid: function clearHintIfInvalid() { - var val, hint, valIsPrefixOfHint, isValid; - val = this.getInputValue(); - hint = this.getHint(); - valIsPrefixOfHint = val !== hint && hint.indexOf(val) === 0; - isValid = val !== "" && valIsPrefixOfHint && !this.hasOverflow(); - !isValid && this.clearHint(); - }, - hasFocus: function hasFocus() { - return this.$input.is(":focus"); - }, - hasOverflow: function hasOverflow() { - var constraint = this.$input.width() - 2; - this.$overflowHelper.text(this.getInputValue()); - return this.$overflowHelper.width() >= constraint; - }, - isCursorAtEnd: function() { - var valueLength, selectionStart, range; - valueLength = this.$input.val().length; - selectionStart = this.$input[0].selectionStart; - if (_.isNumber(selectionStart)) { - return selectionStart === valueLength; - } else if (document.selection) { - range = document.selection.createRange(); - range.moveStart("character", -valueLength); - return valueLength === range.text.length; - } - return true; - }, - destroy: function destroy() { - this.$hint.off(".tt"); - this.$input.off(".tt"); - this.$overflowHelper.remove(); - this.$hint = this.$input = this.$overflowHelper = $("
    "); - } - }); - return Input; - function buildOverflowHelper($input) { - return $('').css({ - position: "absolute", - visibility: "hidden", - whiteSpace: "pre", - fontFamily: $input.css("font-family"), - fontSize: $input.css("font-size"), - fontStyle: $input.css("font-style"), - fontVariant: $input.css("font-variant"), - fontWeight: $input.css("font-weight"), - wordSpacing: $input.css("word-spacing"), - letterSpacing: $input.css("letter-spacing"), - textIndent: $input.css("text-indent"), - textRendering: $input.css("text-rendering"), - textTransform: $input.css("text-transform") - }).insertAfter($input); - } - function areQueriesEquivalent(a, b) { - return Input.normalizeQuery(a) === Input.normalizeQuery(b); - } - function withModifier($e) { - return $e.altKey || $e.ctrlKey || $e.metaKey || $e.shiftKey; - } - }(); - var Dataset = function() { - "use strict"; - var keys, nameGenerator; - keys = { - val: "tt-selectable-display", - obj: "tt-selectable-object" - }; - nameGenerator = _.getIdGenerator(); - function Dataset(o, www) { - o = o || {}; - o.templates = o.templates || {}; - o.templates.notFound = o.templates.notFound || o.templates.empty; - if (!o.source) { - $.error("missing source"); - } - if (!o.node) { - $.error("missing node"); - } - if (o.name && !isValidName(o.name)) { - $.error("invalid dataset name: " + o.name); - } - www.mixin(this); - this.highlight = !!o.highlight; - this.name = o.name || nameGenerator(); - this.limit = o.limit || 5; - this.displayFn = getDisplayFn(o.display || o.displayKey); - this.templates = getTemplates(o.templates, this.displayFn); - this.source = o.source.__ttAdapter ? o.source.__ttAdapter() : o.source; - this.async = _.isUndefined(o.async) ? this.source.length > 2 : !!o.async; - this._resetLastSuggestion(); - this.$el = $(o.node).addClass(this.classes.dataset).addClass(this.classes.dataset + "-" + this.name); - } - Dataset.extractData = function extractData(el) { - var $el = $(el); - if ($el.data(keys.obj)) { - return { - val: $el.data(keys.val) || "", - obj: $el.data(keys.obj) || null - }; - } - return null; - }; - _.mixin(Dataset.prototype, EventEmitter, { - _overwrite: function overwrite(query, suggestions) { - suggestions = suggestions || []; - if (suggestions.length) { - this._renderSuggestions(query, suggestions); - } else if (this.async && this.templates.pending) { - this._renderPending(query); - } else if (!this.async && this.templates.notFound) { - this._renderNotFound(query); - } else { - this._empty(); - } - this.trigger("rendered", this.name, suggestions, false); - }, - _append: function append(query, suggestions) { - suggestions = suggestions || []; - if (suggestions.length && this.$lastSuggestion.length) { - this._appendSuggestions(query, suggestions); - } else if (suggestions.length) { - this._renderSuggestions(query, suggestions); - } else if (!this.$lastSuggestion.length && this.templates.notFound) { - this._renderNotFound(query); - } - this.trigger("rendered", this.name, suggestions, true); - }, - _renderSuggestions: function renderSuggestions(query, suggestions) { - var $fragment; - $fragment = this._getSuggestionsFragment(query, suggestions); - this.$lastSuggestion = $fragment.children().last(); - this.$el.html($fragment).prepend(this._getHeader(query, suggestions)).append(this._getFooter(query, suggestions)); - }, - _appendSuggestions: function appendSuggestions(query, suggestions) { - var $fragment, $lastSuggestion; - $fragment = this._getSuggestionsFragment(query, suggestions); - $lastSuggestion = $fragment.children().last(); - this.$lastSuggestion.after($fragment); - this.$lastSuggestion = $lastSuggestion; - }, - _renderPending: function renderPending(query) { - var template = this.templates.pending; - this._resetLastSuggestion(); - template && this.$el.html(template({ - query: query, - dataset: this.name - })); - }, - _renderNotFound: function renderNotFound(query) { - var template = this.templates.notFound; - this._resetLastSuggestion(); - template && this.$el.html(template({ - query: query, - dataset: this.name - })); - }, - _empty: function empty() { - this.$el.empty(); - this._resetLastSuggestion(); - }, - _getSuggestionsFragment: function getSuggestionsFragment(query, suggestions) { - var that = this, fragment; - fragment = document.createDocumentFragment(); - _.each(suggestions, function getSuggestionNode(suggestion) { - var $el, context; - context = that._injectQuery(query, suggestion); - $el = $(that.templates.suggestion(context)).data(keys.obj, suggestion).data(keys.val, that.displayFn(suggestion)).addClass(that.classes.suggestion + " " + that.classes.selectable); - fragment.appendChild($el[0]); - }); - this.highlight && highlight({ - className: this.classes.highlight, - node: fragment, - pattern: query - }); - return $(fragment); - }, - _getFooter: function getFooter(query, suggestions) { - return this.templates.footer ? this.templates.footer({ - query: query, - suggestions: suggestions, - dataset: this.name - }) : null; - }, - _getHeader: function getHeader(query, suggestions) { - return this.templates.header ? this.templates.header({ - query: query, - suggestions: suggestions, - dataset: this.name - }) : null; - }, - _resetLastSuggestion: function resetLastSuggestion() { - this.$lastSuggestion = $(); - }, - _injectQuery: function injectQuery(query, obj) { - return _.isObject(obj) ? _.mixin({ - _query: query - }, obj) : obj; - }, - update: function update(query) { - var that = this, canceled = false, syncCalled = false, rendered = 0; - this.cancel(); - this.cancel = function cancel() { - canceled = true; - that.cancel = $.noop; - that.async && that.trigger("asyncCanceled", query); - }; - this.source(query, sync, async); - !syncCalled && sync([]); - function sync(suggestions) { - if (syncCalled) { - return; - } - syncCalled = true; - suggestions = (suggestions || []).slice(0, that.limit); - rendered = suggestions.length; - that._overwrite(query, suggestions); - if (rendered < that.limit && that.async) { - that.trigger("asyncRequested", query); - } - } - function async(suggestions) { - suggestions = suggestions || []; - if (!canceled && rendered < that.limit) { - that.cancel = $.noop; - rendered += suggestions.length; - that._append(query, suggestions.slice(0, that.limit - rendered)); - that.async && that.trigger("asyncReceived", query); - } - } - }, - cancel: $.noop, - clear: function clear() { - this._empty(); - this.cancel(); - this.trigger("cleared"); - }, - isEmpty: function isEmpty() { - return this.$el.is(":empty"); - }, - destroy: function destroy() { - this.$el = $("
    "); - } - }); - return Dataset; - function getDisplayFn(display) { - display = display || _.stringify; - return _.isFunction(display) ? display : displayFn; - function displayFn(obj) { - return obj[display]; - } - } - function getTemplates(templates, displayFn) { - return { - notFound: templates.notFound && _.templatify(templates.notFound), - pending: templates.pending && _.templatify(templates.pending), - header: templates.header && _.templatify(templates.header), - footer: templates.footer && _.templatify(templates.footer), - suggestion: templates.suggestion || suggestionTemplate - }; - function suggestionTemplate(context) { - return $("
    ").text(displayFn(context)); - } - } - function isValidName(str) { - return /^[_a-zA-Z0-9-]+$/.test(str); - } - }(); - var Menu = function() { - "use strict"; - function Menu(o, www) { - var that = this; - o = o || {}; - if (!o.node) { - $.error("node is required"); - } - www.mixin(this); - this.$node = $(o.node); - this.query = null; - this.datasets = _.map(o.datasets, initializeDataset); - function initializeDataset(oDataset) { - var node = that.$node.find(oDataset.node).first(); - oDataset.node = node.length ? node : $("
    ").appendTo(that.$node); - return new Dataset(oDataset, www); - } - } - _.mixin(Menu.prototype, EventEmitter, { - _onSelectableClick: function onSelectableClick($e) { - this.trigger("selectableClicked", $($e.currentTarget)); - }, - _onRendered: function onRendered(type, dataset, suggestions, async) { - this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); - this.trigger("datasetRendered", dataset, suggestions, async); - }, - _onCleared: function onCleared() { - this.$node.toggleClass(this.classes.empty, this._allDatasetsEmpty()); - this.trigger("datasetCleared"); - }, - _propagate: function propagate() { - this.trigger.apply(this, arguments); - }, - _allDatasetsEmpty: function allDatasetsEmpty() { - return _.every(this.datasets, isDatasetEmpty); - function isDatasetEmpty(dataset) { - return dataset.isEmpty(); - } - }, - _getSelectables: function getSelectables() { - return this.$node.find(this.selectors.selectable); - }, - _removeCursor: function _removeCursor() { - var $selectable = this.getActiveSelectable(); - $selectable && $selectable.removeClass(this.classes.cursor); - }, - _ensureVisible: function ensureVisible($el) { - var elTop, elBottom, nodeScrollTop, nodeHeight; - elTop = $el.position().top; - elBottom = elTop + $el.outerHeight(true); - nodeScrollTop = this.$node.scrollTop(); - nodeHeight = this.$node.height() + parseInt(this.$node.css("paddingTop"), 10) + parseInt(this.$node.css("paddingBottom"), 10); - if (elTop < 0) { - this.$node.scrollTop(nodeScrollTop + elTop); - } else if (nodeHeight < elBottom) { - this.$node.scrollTop(nodeScrollTop + (elBottom - nodeHeight)); - } - }, - bind: function() { - var that = this, onSelectableClick; - onSelectableClick = _.bind(this._onSelectableClick, this); - this.$node.on("click.tt", this.selectors.selectable, onSelectableClick); - _.each(this.datasets, function(dataset) { - dataset.onSync("asyncRequested", that._propagate, that).onSync("asyncCanceled", that._propagate, that).onSync("asyncReceived", that._propagate, that).onSync("rendered", that._onRendered, that).onSync("cleared", that._onCleared, that); - }); - return this; - }, - isOpen: function isOpen() { - return this.$node.hasClass(this.classes.open); - }, - open: function open() { - this.$node.addClass(this.classes.open); - }, - close: function close() { - this.$node.removeClass(this.classes.open); - this._removeCursor(); - }, - setLanguageDirection: function setLanguageDirection(dir) { - this.$node.attr("dir", dir); - }, - selectableRelativeToCursor: function selectableRelativeToCursor(delta) { - var $selectables, $oldCursor, oldIndex, newIndex; - $oldCursor = this.getActiveSelectable(); - $selectables = this._getSelectables(); - oldIndex = $oldCursor ? $selectables.index($oldCursor) : -1; - newIndex = oldIndex + delta; - newIndex = (newIndex + 1) % ($selectables.length + 1) - 1; - newIndex = newIndex < -1 ? $selectables.length - 1 : newIndex; - return newIndex === -1 ? null : $selectables.eq(newIndex); - }, - setCursor: function setCursor($selectable) { - this._removeCursor(); - if ($selectable = $selectable && $selectable.first()) { - $selectable.addClass(this.classes.cursor); - this._ensureVisible($selectable); - } - }, - getSelectableData: function getSelectableData($el) { - return $el && $el.length ? Dataset.extractData($el) : null; - }, - getActiveSelectable: function getActiveSelectable() { - var $selectable = this._getSelectables().filter(this.selectors.cursor).first(); - return $selectable.length ? $selectable : null; - }, - getTopSelectable: function getTopSelectable() { - var $selectable = this._getSelectables().first(); - return $selectable.length ? $selectable : null; - }, - update: function update(query) { - var isValidUpdate = query !== this.query; - if (isValidUpdate) { - this.query = query; - _.each(this.datasets, updateDataset); - } - return isValidUpdate; - function updateDataset(dataset) { - dataset.update(query); - } - }, - empty: function empty() { - _.each(this.datasets, clearDataset); - this.query = null; - this.$node.addClass(this.classes.empty); - function clearDataset(dataset) { - dataset.clear(); - } - }, - destroy: function destroy() { - this.$node.off(".tt"); - this.$node = $("
    "); - _.each(this.datasets, destroyDataset); - function destroyDataset(dataset) { - dataset.destroy(); - } - } - }); - return Menu; - }(); - var DefaultMenu = function() { - "use strict"; - var s = Menu.prototype; - function DefaultMenu() { - Menu.apply(this, [].slice.call(arguments, 0)); - } - _.mixin(DefaultMenu.prototype, Menu.prototype, { - open: function open() { - !this._allDatasetsEmpty() && this._show(); - return s.open.apply(this, [].slice.call(arguments, 0)); - }, - close: function close() { - this._hide(); - return s.close.apply(this, [].slice.call(arguments, 0)); - }, - _onRendered: function onRendered() { - if (this._allDatasetsEmpty()) { - this._hide(); - } else { - this.isOpen() && this._show(); - } - return s._onRendered.apply(this, [].slice.call(arguments, 0)); - }, - _onCleared: function onCleared() { - if (this._allDatasetsEmpty()) { - this._hide(); - } else { - this.isOpen() && this._show(); - } - return s._onCleared.apply(this, [].slice.call(arguments, 0)); - }, - setLanguageDirection: function setLanguageDirection(dir) { - this.$node.css(dir === "ltr" ? this.css.ltr : this.css.rtl); - return s.setLanguageDirection.apply(this, [].slice.call(arguments, 0)); - }, - _hide: function hide() { - this.$node.hide(); - }, - _show: function show() { - this.$node.css("display", "block"); - } - }); - return DefaultMenu; - }(); - var Typeahead = function() { - "use strict"; - function Typeahead(o, www) { - var onFocused, onBlurred, onEnterKeyed, onTabKeyed, onEscKeyed, onUpKeyed, onDownKeyed, onLeftKeyed, onRightKeyed, onQueryChanged, onWhitespaceChanged; - o = o || {}; - if (!o.input) { - $.error("missing input"); - } - if (!o.menu) { - $.error("missing menu"); - } - if (!o.eventBus) { - $.error("missing event bus"); - } - www.mixin(this); - this.eventBus = o.eventBus; - this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; - this.input = o.input; - this.menu = o.menu; - this.enabled = true; - this.active = false; - this.input.hasFocus() && this.activate(); - this.dir = this.input.getLangDir(); - this._hacks(); - this.menu.bind().onSync("selectableClicked", this._onSelectableClicked, this).onSync("asyncRequested", this._onAsyncRequested, this).onSync("asyncCanceled", this._onAsyncCanceled, this).onSync("asyncReceived", this._onAsyncReceived, this).onSync("datasetRendered", this._onDatasetRendered, this).onSync("datasetCleared", this._onDatasetCleared, this); - onFocused = c(this, "activate", "open", "_onFocused"); - onBlurred = c(this, "deactivate", "_onBlurred"); - onEnterKeyed = c(this, "isActive", "isOpen", "_onEnterKeyed"); - onTabKeyed = c(this, "isActive", "isOpen", "_onTabKeyed"); - onEscKeyed = c(this, "isActive", "_onEscKeyed"); - onUpKeyed = c(this, "isActive", "open", "_onUpKeyed"); - onDownKeyed = c(this, "isActive", "open", "_onDownKeyed"); - onLeftKeyed = c(this, "isActive", "isOpen", "_onLeftKeyed"); - onRightKeyed = c(this, "isActive", "isOpen", "_onRightKeyed"); - onQueryChanged = c(this, "_openIfActive", "_onQueryChanged"); - onWhitespaceChanged = c(this, "_openIfActive", "_onWhitespaceChanged"); - this.input.bind().onSync("focused", onFocused, this).onSync("blurred", onBlurred, this).onSync("enterKeyed", onEnterKeyed, this).onSync("tabKeyed", onTabKeyed, this).onSync("escKeyed", onEscKeyed, this).onSync("upKeyed", onUpKeyed, this).onSync("downKeyed", onDownKeyed, this).onSync("leftKeyed", onLeftKeyed, this).onSync("rightKeyed", onRightKeyed, this).onSync("queryChanged", onQueryChanged, this).onSync("whitespaceChanged", onWhitespaceChanged, this).onSync("langDirChanged", this._onLangDirChanged, this); - } - _.mixin(Typeahead.prototype, { - _hacks: function hacks() { - var $input, $menu; - $input = this.input.$input || $("
    "); - $menu = this.menu.$node || $("
    "); - $input.on("blur.tt", function($e) { - var active, isActive, hasActive; - active = document.activeElement; - isActive = $menu.is(active); - hasActive = $menu.has(active).length > 0; - if (_.isMsie() && (isActive || hasActive)) { - $e.preventDefault(); - $e.stopImmediatePropagation(); - _.defer(function() { - $input.focus(); - }); - } - }); - $menu.on("mousedown.tt", function($e) { - $e.preventDefault(); - }); - }, - _onSelectableClicked: function onSelectableClicked(type, $el) { - this.select($el); - }, - _onDatasetCleared: function onDatasetCleared() { - this._updateHint(); - }, - _onDatasetRendered: function onDatasetRendered(type, dataset, suggestions, async) { - this._updateHint(); - this.eventBus.trigger("render", suggestions, async, dataset); - }, - _onAsyncRequested: function onAsyncRequested(type, dataset, query) { - this.eventBus.trigger("asyncrequest", query, dataset); - }, - _onAsyncCanceled: function onAsyncCanceled(type, dataset, query) { - this.eventBus.trigger("asynccancel", query, dataset); - }, - _onAsyncReceived: function onAsyncReceived(type, dataset, query) { - this.eventBus.trigger("asyncreceive", query, dataset); - }, - _onFocused: function onFocused() { - this._minLengthMet() && this.menu.update(this.input.getQuery()); - }, - _onBlurred: function onBlurred() { - if (this.input.hasQueryChangedSinceLastFocus()) { - this.eventBus.trigger("change", this.input.getQuery()); - } - }, - _onEnterKeyed: function onEnterKeyed(type, $e) { - var $selectable; - if ($selectable = this.menu.getActiveSelectable()) { - this.select($selectable) && $e.preventDefault(); - } - }, - _onTabKeyed: function onTabKeyed(type, $e) { - var $selectable; - if ($selectable = this.menu.getActiveSelectable()) { - this.select($selectable) && $e.preventDefault(); - } else if ($selectable = this.menu.getTopSelectable()) { - this.autocomplete($selectable) && $e.preventDefault(); - } - }, - _onEscKeyed: function onEscKeyed() { - this.close(); - }, - _onUpKeyed: function onUpKeyed() { - this.moveCursor(-1); - }, - _onDownKeyed: function onDownKeyed() { - this.moveCursor(+1); - }, - _onLeftKeyed: function onLeftKeyed() { - if (this.dir === "rtl" && this.input.isCursorAtEnd()) { - this.autocomplete(this.menu.getTopSelectable()); - } - }, - _onRightKeyed: function onRightKeyed() { - if (this.dir === "ltr" && this.input.isCursorAtEnd()) { - this.autocomplete(this.menu.getTopSelectable()); - } - }, - _onQueryChanged: function onQueryChanged(e, query) { - this._minLengthMet(query) ? this.menu.update(query) : this.menu.empty(); - }, - _onWhitespaceChanged: function onWhitespaceChanged() { - this._updateHint(); - }, - _onLangDirChanged: function onLangDirChanged(e, dir) { - if (this.dir !== dir) { - this.dir = dir; - this.menu.setLanguageDirection(dir); - } - }, - _openIfActive: function openIfActive() { - this.isActive() && this.open(); - }, - _minLengthMet: function minLengthMet(query) { - query = _.isString(query) ? query : this.input.getQuery() || ""; - return query.length >= this.minLength; - }, - _updateHint: function updateHint() { - var $selectable, data, val, query, escapedQuery, frontMatchRegEx, match; - $selectable = this.menu.getTopSelectable(); - data = this.menu.getSelectableData($selectable); - val = this.input.getInputValue(); - if (data && !_.isBlankString(val) && !this.input.hasOverflow()) { - query = Input.normalizeQuery(val); - escapedQuery = _.escapeRegExChars(query); - frontMatchRegEx = new RegExp("^(?:" + escapedQuery + ")(.+$)", "i"); - match = frontMatchRegEx.exec(data.val); - match && this.input.setHint(val + match[1]); - } else { - this.input.clearHint(); - } - }, - isEnabled: function isEnabled() { - return this.enabled; - }, - enable: function enable() { - this.enabled = true; - }, - disable: function disable() { - this.enabled = false; - }, - isActive: function isActive() { - return this.active; - }, - activate: function activate() { - if (this.isActive()) { - return true; - } else if (!this.isEnabled() || this.eventBus.before("active")) { - return false; - } else { - this.active = true; - this.eventBus.trigger("active"); - return true; - } - }, - deactivate: function deactivate() { - if (!this.isActive()) { - return true; - } else if (this.eventBus.before("idle")) { - return false; - } else { - this.active = false; - this.close(); - this.eventBus.trigger("idle"); - return true; - } - }, - isOpen: function isOpen() { - return this.menu.isOpen(); - }, - open: function open() { - if (!this.isOpen() && !this.eventBus.before("open")) { - this.menu.open(); - this._updateHint(); - this.eventBus.trigger("open"); - } - return this.isOpen(); - }, - close: function close() { - if (this.isOpen() && !this.eventBus.before("close")) { - this.menu.close(); - this.input.clearHint(); - this.input.resetInputValue(); - this.eventBus.trigger("close"); - } - return !this.isOpen(); - }, - setVal: function setVal(val) { - this.input.setQuery(_.toStr(val)); - }, - getVal: function getVal() { - return this.input.getQuery(); - }, - select: function select($selectable) { - var data = this.menu.getSelectableData($selectable); - if (data && !this.eventBus.before("select", data.obj)) { - this.input.setQuery(data.val, true); - this.eventBus.trigger("select", data.obj); - this.close(); - return true; - } - return false; - }, - autocomplete: function autocomplete($selectable) { - var query, data, isValid; - query = this.input.getQuery(); - data = this.menu.getSelectableData($selectable); - isValid = data && query !== data.val; - if (isValid && !this.eventBus.before("autocomplete", data.obj)) { - this.input.setQuery(data.val); - this.eventBus.trigger("autocomplete", data.obj); - return true; - } - return false; - }, - moveCursor: function moveCursor(delta) { - var query, $candidate, data, payload, cancelMove; - query = this.input.getQuery(); - $candidate = this.menu.selectableRelativeToCursor(delta); - data = this.menu.getSelectableData($candidate); - payload = data ? data.obj : null; - cancelMove = this._minLengthMet() && this.menu.update(query); - if (!cancelMove && !this.eventBus.before("cursorchange", payload)) { - this.menu.setCursor($candidate); - if (data) { - this.input.setInputValue(data.val); - } else { - this.input.resetInputValue(); - this._updateHint(); - } - this.eventBus.trigger("cursorchange", payload); - return true; - } - return false; - }, - destroy: function destroy() { - this.input.destroy(); - this.menu.destroy(); - } - }); - return Typeahead; - function c(ctx) { - var methods = [].slice.call(arguments, 1); - return function() { - var args = [].slice.call(arguments); - _.each(methods, function(method) { - return ctx[method].apply(ctx, args); - }); - }; - } - }(); - (function() { - "use strict"; - var old, keys, methods; - old = $.fn.typeahead; - keys = { - www: "tt-www", - attrs: "tt-attrs", - typeahead: "tt-typeahead" - }; - methods = { - initialize: function initialize(o, datasets) { - var www; - datasets = _.isArray(datasets) ? datasets : [].slice.call(arguments, 1); - o = o || {}; - www = WWW(o.classNames); - return this.each(attach); - function attach() { - var $input, $wrapper, $hint, $menu, defaultHint, defaultMenu, eventBus, input, menu, typeahead, MenuConstructor; - _.each(datasets, function(d) { - d.highlight = !!o.highlight; - }); - $input = $(this); - $wrapper = $(www.html.wrapper); - $hint = $elOrNull(o.hint); - $menu = $elOrNull(o.menu); - defaultHint = o.hint !== false && !$hint; - defaultMenu = o.menu !== false && !$menu; - defaultHint && ($hint = buildHintFromInput($input, www)); - defaultMenu && ($menu = $(www.html.menu).css(www.css.menu)); - $hint && $hint.val(""); - $input = prepInput($input, www); - if (defaultHint || defaultMenu) { - $wrapper.css(www.css.wrapper); - $input.css(defaultHint ? www.css.input : www.css.inputWithNoHint); - $input.wrap($wrapper).parent().prepend(defaultHint ? $hint : null).append(defaultMenu ? $menu : null); - } - MenuConstructor = defaultMenu ? DefaultMenu : Menu; - eventBus = new EventBus({ - el: $input - }); - input = new Input({ - hint: $hint, - input: $input - }, www); - menu = new MenuConstructor({ - node: $menu, - datasets: datasets - }, www); - typeahead = new Typeahead({ - input: input, - menu: menu, - eventBus: eventBus, - minLength: o.minLength - }, www); - $input.data(keys.www, www); - $input.data(keys.typeahead, typeahead); - } - }, - isEnabled: function isEnabled() { - var enabled; - ttEach(this.first(), function(t) { - enabled = t.isEnabled(); - }); - return enabled; - }, - enable: function enable() { - ttEach(this, function(t) { - t.enable(); - }); - return this; - }, - disable: function disable() { - ttEach(this, function(t) { - t.disable(); - }); - return this; - }, - isActive: function isActive() { - var active; - ttEach(this.first(), function(t) { - active = t.isActive(); - }); - return active; - }, - activate: function activate() { - ttEach(this, function(t) { - t.activate(); - }); - return this; - }, - deactivate: function deactivate() { - ttEach(this, function(t) { - t.deactivate(); - }); - return this; - }, - isOpen: function isOpen() { - var open; - ttEach(this.first(), function(t) { - open = t.isOpen(); - }); - return open; - }, - open: function open() { - ttEach(this, function(t) { - t.open(); - }); - return this; - }, - close: function close() { - ttEach(this, function(t) { - t.close(); - }); - return this; - }, - select: function select(el) { - var success = false, $el = $(el); - ttEach(this.first(), function(t) { - success = t.select($el); - }); - return success; - }, - autocomplete: function autocomplete(el) { - var success = false, $el = $(el); - ttEach(this.first(), function(t) { - success = t.autocomplete($el); - }); - return success; - }, - moveCursor: function moveCursoe(delta) { - var success = false; - ttEach(this.first(), function(t) { - success = t.moveCursor(delta); - }); - return success; - }, - val: function val(newVal) { - var query; - if (!arguments.length) { - ttEach(this.first(), function(t) { - query = t.getVal(); - }); - return query; - } else { - ttEach(this, function(t) { - t.setVal(newVal); - }); - return this; - } - }, - destroy: function destroy() { - ttEach(this, function(typeahead, $input) { - revert($input); - typeahead.destroy(); - }); - return this; - } - }; - $.fn.typeahead = function(method) { - if (methods[method]) { - return methods[method].apply(this, [].slice.call(arguments, 1)); - } else { - return methods.initialize.apply(this, arguments); - } - }; - $.fn.typeahead.noConflict = function noConflict() { - $.fn.typeahead = old; - return this; - }; - function ttEach($els, fn) { - $els.each(function() { - var $input = $(this), typeahead; - (typeahead = $input.data(keys.typeahead)) && fn(typeahead, $input); - }); - } - function buildHintFromInput($input, www) { - return $input.clone().addClass(www.classes.hint).removeData().css(www.css.hint).css(getBackgroundStyles($input)).prop("readonly", true).removeAttr("id name placeholder required").attr({ - autocomplete: "off", - spellcheck: "false", - tabindex: -1 - }); - } - function prepInput($input, www) { - $input.data(keys.attrs, { - dir: $input.attr("dir"), - autocomplete: $input.attr("autocomplete"), - spellcheck: $input.attr("spellcheck"), - style: $input.attr("style") - }); - $input.addClass(www.classes.input).attr({ - autocomplete: "off", - spellcheck: false - }); - try { - !$input.attr("dir") && $input.attr("dir", "auto"); - } catch (e) {} - return $input; - } - function getBackgroundStyles($el) { - return { - backgroundAttachment: $el.css("background-attachment"), - backgroundClip: $el.css("background-clip"), - backgroundColor: $el.css("background-color"), - backgroundImage: $el.css("background-image"), - backgroundOrigin: $el.css("background-origin"), - backgroundPosition: $el.css("background-position"), - backgroundRepeat: $el.css("background-repeat"), - backgroundSize: $el.css("background-size") - }; - } - function revert($input) { - var www, $wrapper; - www = $input.data(keys.www); - $wrapper = $input.parent().filter(www.selectors.wrapper); - _.each($input.data(keys.attrs), function(val, key) { - _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); - }); - $input.removeData(keys.typeahead).removeData(keys.www).removeData(keys.attr).removeClass(www.classes.input); - if ($wrapper.length) { - $input.detach().insertAfter($wrapper); - $wrapper.remove(); - } - } - function $elOrNull(obj) { - var isValid, $el; - isValid = _.isJQuery(obj) || _.isElement(obj); - $el = isValid ? $(obj).first() : []; - return $el.length ? $el : null; - } - })(); -}); diff --git a/templates/base.html b/templates/base.html index be46cccb..36fc45e4 100644 --- a/templates/base.html +++ b/templates/base.html @@ -45,16 +45,13 @@ with this program; if not, write to the Free Software Foundation, Inc., {# Preload JavaScript #} {% bootstrap_javascript %} - - {% block custom_js %}{% endblock %} {# Load CSS #} {% bootstrap_css %} - - + {# load theme #} {% if request.user.is_authenticated %} @@ -118,4 +115,4 @@ with this program; if not, write to the Free Software Foundation, Inc., - \ No newline at end of file + From a27e562a59656fe41f15d582c6173b196481b9d8 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 28 Dec 2020 22:29:59 +0100 Subject: [PATCH 421/490] Add dal to installed apps --- re2o/settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/re2o/settings.py b/re2o/settings.py index f7975ae4..2e3d75a7 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -59,6 +59,8 @@ LOGIN_URL = "/login/" # The URL for login page LOGIN_REDIRECT_URL = "/" # The URL for redirecting after login # Application definition +# dal_legacy_static only needed for Django < 2.0 (https://django-autocomplete-light.readthedocs.io/en/master/install.html#django-versions-earlier-than-2-0) +EARLY_EXTERNAL_CONTRIB_APPS = ("dal", "dal_select2", "dal_legacy_static") # Need to be added before django.contrib.admin (https://django-autocomplete-light.readthedocs.io/en/master/install.html#configuration) DJANGO_CONTRIB_APPS = ( "django.contrib.admin", "django.contrib.auth", @@ -80,7 +82,7 @@ LOCAL_APPS = ( "logs", ) INSTALLED_APPS = ( - DJANGO_CONTRIB_APPS + EXTERNAL_CONTRIB_APPS + LOCAL_APPS + OPTIONNAL_APPS + EARLY_EXTERNAL_CONTRIB_APPS + DJANGO_CONTRIB_APPS + EXTERNAL_CONTRIB_APPS + LOCAL_APPS + OPTIONNAL_APPS ) MIDDLEWARE_CLASSES = ( "django.contrib.sessions.middleware.SessionMiddleware", From b12fc190ba646db0fd212ce75ca990ae88faa2c2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 28 Dec 2020 22:30:17 +0100 Subject: [PATCH 422/490] Create dal mixins --- re2o/mixins.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/re2o/mixins.py b/re2o/mixins.py index 26034e07..a57a0628 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -26,6 +26,7 @@ A set of mixins used all over the project to avoid duplicating code from reversion import revisions as reversion from django.db import transaction from django.utils.translation import ugettext as _ +from dal import autocomplete class RevMixin(object): @@ -228,3 +229,56 @@ class AclMixin(object): else None, (permission,), ) + + +class AutocompleteModelMixin(autocomplete.ModelSelect2): + """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options + See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 + """ + def __init__(self, *args, **kwargs): + select2_attrs = kwargs.get("attrs", {}) + kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) + + super().__init__(*args, **kwargs) + + def fill_default_select2_attrs(self, attrs): + """ + See https://select2.org/configuration/options-api + """ + # By default, only trigger autocompletion after 3 characters have been typed + #attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # If there are less than 10 results, just show all of them (no need to autocomplete) + attrs["data-minimum-results-for-search"] = attrs.get("data-minimum-results-for-search", 10) + return attrs + + +class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): + """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options + See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 + """ + def __init__(self, *args, **kwargs): + select2_attrs = kwargs.get("attrs", {}) + kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) + + super().__init__(*args, **kwargs) + + def fill_default_select2_attrs(self, attrs): + """ + See https://select2.org/configuration/options-api + """ + # By default, only trigger autocompletion after 3 characters have been typed + #attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # If there are less than 10 results, just show all of them (no need to autocomplete) + attrs["data-minimum-results-for-search"] = attrs.get("data-minimum-results-for-search", 10) + return attrs + + +class AutocompleteViewMixin(autocomplete.Select2QuerySetView): + obj_type = None # This MUST be overridden by child class + query_filter = "name__icontains" # Override this if necessary + + def get_queryset(self): + query_set = self.obj_type.objects.all() + if self.q: + query_set = query_set.filter(**{ self.query_filter: self.q}) + return query_set From 1f83a8eff20afca3a860af1815da874f7fd21f58 Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 18:49:52 +0100 Subject: [PATCH 423/490] Rename vars --- logs/forms.py | 16 ++++++++-------- logs/models.py | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index b8c8b010..c98bd282 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -114,28 +114,28 @@ def classes_for_action_type(action_type): class ActionsSearchForm(Form): """Form used to do an advanced search through the logs.""" - u = forms.ModelChoiceField( + user = forms.ModelChoiceField( label=_("Performed by"), queryset=users.models.User.objects.all(), required=False, ) - t = forms.MultipleChoiceField( + action_type = forms.MultipleChoiceField( label=_("Action type"), required=False, widget=forms.CheckboxSelectMultiple, choices=CHOICES_ACTION_TYPE, initial=[i[0] for i in CHOICES_ACTION_TYPE], ) - s = forms.DateField(required=False, label=_("Start date")) - e = forms.DateField(required=False, label=_("End date")) + start_date = forms.DateField(required=False, label=_("Start date")) + end_date = forms.DateField(required=False, label=_("End date")) def __init__(self, *args, **kwargs): super(ActionsSearchForm, self).__init__(*args, **kwargs) - self.fields["s"].help_text = get_input_formats_help_text( - self.fields["s"].input_formats + self.fields["start_date"].help_text = get_input_formats_help_text( + self.fields["start_date"].input_formats ) - self.fields["e"].help_text = get_input_formats_help_text( - self.fields["e"].input_formats + self.fields["end_date"].help_text = get_input_formats_help_text( + self.fields["end_date"].input_formats ) diff --git a/logs/models.py b/logs/models.py index 8790f46e..aa5d7822 100644 --- a/logs/models.py +++ b/logs/models.py @@ -600,10 +600,10 @@ class ActionsSearch: Returns: The QuerySet of Revision objects corresponding to the search. """ - user = params.get("u", None) - start = params.get("s", None) - end = params.get("e", None) - action_types = params.get("t", None) + user = params.get("user", None) + start = params.get("start_date", None) + end = params.get("end_date", None) + action_types = params.get("action_type", None) query = Q() From b418719ef7bb0d8d1f0eca37df793b702114167e Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 18:54:50 +0100 Subject: [PATCH 424/490] Autocomplete on history search --- logs/forms.py | 2 ++ logs/templates/logs/search_stats_logs.html | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/logs/forms.py b/logs/forms.py index c98bd282..eb623827 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -25,6 +25,7 @@ from django import forms from django.forms import Form from django.utils.translation import ugettext_lazy as _ from re2o.base import get_input_formats_help_text +from re2o.mixins import AutocompleteModelMixin import inspect @@ -118,6 +119,7 @@ class ActionsSearchForm(Form): label=_("Performed by"), queryset=users.models.User.objects.all(), required=False, + widget=AutocompleteModelMixin(url="/users/user-autocomplete"), ) action_type = forms.MultipleChoiceField( label=_("Action type"), diff --git a/logs/templates/logs/search_stats_logs.html b/logs/templates/logs/search_stats_logs.html index b124139e..88c2babd 100644 --- a/logs/templates/logs/search_stats_logs.html +++ b/logs/templates/logs/search_stats_logs.html @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load i18n %} {% block title %}{% trans "Search events" %}{% endblock %} @@ -32,10 +31,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "Search events" %}

    - {% massive_bootstrap_form actions_form 'u' %} + {% bootstrap_form actions_form %} {% trans "Search" as tr_search %} {% bootstrap_button tr_search button_type="submit" icon="search" %} +{{ actions_form.media }}


    From 4df53ca90269de198bfdf85255fbfb5b90830559 Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 19:06:09 +0100 Subject: [PATCH 425/490] Add can_list acl, move views autocomplete mixins to re2o/views.py --- cotisations/views_autocomplete.py | 2 +- re2o/acl.py | 9 ++++++++ re2o/mixins.py | 35 ++++++++++++++++++++++--------- re2o/templatetags/acl.py | 3 +++ re2o/views.py | 23 ++++++++++++++++++++ topologie/views_autocomplete.py | 2 +- users/views_autocomplete.py | 2 +- 7 files changed, 63 insertions(+), 13 deletions(-) diff --git a/cotisations/views_autocomplete.py b/cotisations/views_autocomplete.py index 70afe855..1e634813 100644 --- a/cotisations/views_autocomplete.py +++ b/cotisations/views_autocomplete.py @@ -37,7 +37,7 @@ from .models import ( Banque ) -from re2o.mixins import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin from re2o.acl import ( can_view_all, diff --git a/re2o/acl.py b/re2o/acl.py index f74427ea..692df905 100644 --- a/re2o/acl.py +++ b/re2o/acl.py @@ -369,6 +369,15 @@ def can_view_all(*targets): return acl_base_decorator("can_view_all", *targets, on_instance=False) +def can_list(*targets): + """Decorator to check if an user can list a class of model. + It runs `acl_base_decorator` with the flag `on_instance=False` and the + method 'can_list'. See `acl_base_decorator` documentation for further + details. + """ + return acl_base_decorator("can_list", *targets, on_instance=False) + + def can_view_app(*apps_name): """Decorator to check if an user can view the applications.""" for app_name in apps_name: diff --git a/re2o/mixins.py b/re2o/mixins.py index a57a0628..df5d7310 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -29,6 +29,7 @@ from django.utils.translation import ugettext as _ from dal import autocomplete + class RevMixin(object): """A mixin to subclass the save and delete function of a model to enforce the versioning of the object before those actions @@ -80,6 +81,8 @@ class AclMixin(object): :can_view: Applied on an instance, return if the user can view the instance :can_view_all: Applied on a class, return if the user can view all + instances + :can_list: Applied on a class, return if the user can list all instances""" @classmethod @@ -209,6 +212,28 @@ class AclMixin(object): (permission,), ) + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """Check if a user can list all instances of an object + + Parameters: + user_request: User calling for this action + + Returns: + Boolean: True if user_request has the right access to do it, else + false with reason for reject authorization + """ + permission = cls.get_modulename() + ".view_" + cls.get_classname() + can = user_request.has_perm(permission) + return ( + can, + _("You don't have the right to list every %s object.") % cls.get_classname() + if not can + else None, + (permission,), + cls.objects.all() if can else None, + ) + def can_view(self, user_request, *_args, **_kwargs): """Check if a user can view an instance of an object @@ -272,13 +297,3 @@ class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): attrs["data-minimum-results-for-search"] = attrs.get("data-minimum-results-for-search", 10) return attrs - -class AutocompleteViewMixin(autocomplete.Select2QuerySetView): - obj_type = None # This MUST be overridden by child class - query_filter = "name__icontains" # Override this if necessary - - def get_queryset(self): - query_set = self.obj_type.objects.all() - if self.q: - query_set = query_set.filter(**{ self.query_filter: self.q}) - return query_set diff --git a/re2o/templatetags/acl.py b/re2o/templatetags/acl.py index 132239be..4c88b207 100644 --- a/re2o/templatetags/acl.py +++ b/re2o/templatetags/acl.py @@ -141,6 +141,8 @@ def get_callback(tag_name, obj=None): return acl_fct(obj.can_view_all, False) if tag_name == "cannot_view_all": return acl_fct(obj.can_view_all, True) + if tag_name == "can_list": + return acl_fct(obj.can_list, False) if tag_name == "can_view_app": return acl_fct( lambda x: ( @@ -296,6 +298,7 @@ def acl_change_filter(parser, token): @register.tag("cannot_delete_all") @register.tag("can_view_all") @register.tag("cannot_view_all") +@register.tag("can_list") def acl_model_filter(parser, token): """Generic definition of an acl templatetag for acl based on model""" diff --git a/re2o/views.py b/re2o/views.py index 51f36b1a..359c3e81 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -33,6 +33,10 @@ from django.template.context_processors import csrf from django.conf import settings from django.utils.translation import ugettext as _ from django.views.decorators.cache import cache_page +from django.contrib.auth.decorators import login_required +from django.contrib.auth.mixins import LoginRequiredMixin +from django.utils.decorators import method_decorator +from dal import autocomplete from preferences.models import ( Service, @@ -43,6 +47,7 @@ from preferences.models import ( Mandate, ) +from .acl import can_list from .contributors import CONTRIBUTORS from importlib import import_module from re2o.settings_local import OPTIONNAL_APPS_RE2O @@ -169,3 +174,21 @@ def handler500(request): def handler404(request): """The handler view for a 404 error""" return render(request, "errors/404.html", status=404) + + +class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView): + obj_type = None # This MUST be overridden by child class + query_set = None + query_filter = "name__icontains" # Override this if necessary + + def get_queryset(self): + + can, reason, _permission, query_set = self.obj_type.can_list(self.request.user) + + self.query_set = query_set + if hasattr(self, "filter_results"): + self.filter_results() + else: + if self.q: + self.query_set = self.query_set.filter(**{ self.query_filter: self.q}) + return self.query_set diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index bef31fdb..801b1572 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -44,7 +44,7 @@ from .models import ( SwitchBay, ) -from re2o.mixins import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin from re2o.acl import ( can_view_all, diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index 94f5a93f..b565a4ce 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -45,7 +45,7 @@ from .models import ( EMailAddress, ) -from re2o.mixins import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin from re2o.acl import ( can_view_all, From 89ca6d231fccf9e23f7b7b0cb3f58bf329e3613a Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 19:22:43 +0100 Subject: [PATCH 426/490] Replace get_queryset with filter_results --- machines/views_autocomplete.py | 22 +++++++++------------ topologie/views_autocomplete.py | 34 ++++++++++++++++----------------- users/views_autocomplete.py | 13 ++++++------- 3 files changed, 31 insertions(+), 38 deletions(-) diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index e68a11c9..ef259da3 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -46,7 +46,7 @@ from .models import ( IpList ) -from re2o.mixins import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin from re2o.acl import ( can_view_all, @@ -84,29 +84,25 @@ class OuverturePortListAutocomplete(AutocompleteViewMixin): class InterfaceAutocomplete(AutocompleteViewMixin): obj_type = Interface - def get_queryset(self): - qs = self.obj_type.objects.all() - + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(domain__name__icontains=self.q) | Q(machine__name__icontains=self.q) ) - return qs - class IpListAutocomplete(AutocompleteViewMixin): obj_type = IpList - def get_queryset(self): + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): machine_type = self.forwarded.get('machine_type', None) - qs = self.obj_type.objects.filter(interface__isnull=True) + self.query_set = self.query_set.filter(interface__isnull=True) if machine_type: - qs = qs.filter(ip_type__machinetype__id=machine_type) + self.query_set = self.query_set.filter(ip_type__machinetype__id=machine_type) if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(ipv4__startswith=self.q) ) - - return qs diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index 801b1572..7f8553c1 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -55,11 +55,11 @@ from re2o.acl import ( class RoomAutocomplete(AutocompleteViewMixin): obj_type = Room - # Override get_queryset to add annotations so search behaves more like users expect it to - def get_queryset(self): + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): # Suppose we have a dorm named Dorm, a building name B, and rooms from 001 - 999 # Comments explain what we try to match - qs = self.obj_type.objects.annotate( + self.query_set = self.query_set.annotate( full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "B 001" full_name_stuck=Concat("building__name", "name"), # Match "B001" dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm 001" @@ -68,7 +68,7 @@ class RoomAutocomplete(AutocompleteViewMixin): ).all() if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(full_name__icontains=self.q) | Q(full_name_stuck__icontains=self.q) | Q(dorm_name__icontains=self.q) @@ -76,8 +76,6 @@ class RoomAutocomplete(AutocompleteViewMixin): | Q(dorm_full_colon_name__icontains=self.q) ) - return qs - #@can_view_all(Dormitory) class DormitoryAutocomplete(AutocompleteViewMixin): @@ -88,20 +86,20 @@ class DormitoryAutocomplete(AutocompleteViewMixin): class BuildingAutocomplete(AutocompleteViewMixin): obj_type = Building - def get_queryset(self): + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): # We want to be able to filter by dorm so it's easier - qs = self.obj_type.objects.annotate( + self.query_set = self.query_set.annotate( full_name=Concat("dormitory__name", Value(" "), "name"), full_name_colon=Concat("dormitory__name", Value(" : "), "name"), ).all() if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(full_name__icontains=self.q) | Q(full_name_colon__icontains=self.q) ) - return qs class SwitchAutocomplete(AutocompleteViewMixin): obj_type = Switch @@ -110,38 +108,38 @@ class SwitchAutocomplete(AutocompleteViewMixin): class PortAutocomplete(AutocompleteViewMixin): obj_type = Port - def get_queryset(self): + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): # We want to enter the switch name, not just the port number # Because we're concatenating a CharField and an Integer, we have to sepcify the output_field - qs = self.obj_type.objects.annotate( + self.query_set = self.query_set.annotate( full_name=Concat("switch__name", Value(" "), "port", output_field=CharField()), full_name_stuck=Concat("switch__name", "port", output_field=CharField()), full_name_dash=Concat("switch__name", Value(" - "), "port", output_field=CharField()), ).all() if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(full_name__icontains=self.q) | Q(full_name_stuck__icontains=self.q) | Q(full_name_dash__icontains=self.q) ) - return qs - class SwitchBayAutocomplete(AutocompleteViewMixin): obj_type = SwitchBay - def get_queryset(self): + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): # Comments explain what we try to match - qs = self.obj_type.objects.annotate( + self.query_set = self.query_set.annotate( full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "" dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm Local Sud" dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm J Local Sud" ).all() if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(full_name__icontains=self.q) | Q(dorm_name__icontains=self.q) | Q(dorm_full_name__icontains=self.q) diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index b565a4ce..f0cfc3ae 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -61,24 +61,23 @@ class SchoolAutocomplete(AutocompleteViewMixin): #@can_view_all(User) class UserAutocomplete(AutocompleteViewMixin): obj_type = User - # Override get_queryset to add annotations so search behaves more like users expect it to - def get_queryset(self): + + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): # Comments explain what we try to match - qs = self.obj_type.objects.annotate( + self.query_set = self.query_set.annotate( full_name=Concat("adherent__name", Value(" "), "surname"), # Match when the user searches "Toto Passoir" full_name_reverse=Concat("surname", Value(" "), "adherent__name"), # Match when the user searches "Passoir Toto" ).all() if self.q: - qs = qs.filter( + self.query_set = self.query_set.filter( Q(pseudo__icontains=self.q) | Q(full_name__icontains=self.q) | Q(full_name_reverse__icontains=self.q) ) - return qs - -#@can_view_all(Adherent) +#can_view_all(Adherent) class AdherentAutocomplete(AutocompleteViewMixin): obj_type = Adherent From c4429a8f1c528877d545ef6548c96c1509f02b17 Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 19:24:12 +0100 Subject: [PATCH 427/490] Remove massive_bf_form --- users/templates/users/plugin_out.html | 1 - users/templates/users/user_autocapture.html | 1 - 2 files changed, 2 deletions(-) diff --git a/users/templates/users/plugin_out.html b/users/templates/users/plugin_out.html index 585c1a0a..6b96896d 100644 --- a/users/templates/users/plugin_out.html +++ b/users/templates/users/plugin_out.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load static %} {% load i18n %} diff --git a/users/templates/users/user_autocapture.html b/users/templates/users/user_autocapture.html index c396a288..44fa1f41 100644 --- a/users/templates/users/user_autocapture.html +++ b/users/templates/users/user_autocapture.html @@ -24,7 +24,6 @@ with this program; if not, write to the Free Software Foundation, Inc., {% endcomment %} {% load bootstrap3 %} -{% load massive_bootstrap_form %} {% load static %} {% load i18n %} {% block title %}{% trans "Users" %}{% endblock %} From 6f121f09d0e001648a446a8aebf0c343ef345c7c Mon Sep 17 00:00:00 2001 From: chirac Date: Wed, 30 Dec 2020 19:29:56 +0100 Subject: [PATCH 428/490] Add autocomplete on multiop --- multi_op/preferences/forms.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/multi_op/preferences/forms.py b/multi_op/preferences/forms.py index 406a9c3b..30aceb36 100644 --- a/multi_op/preferences/forms.py +++ b/multi_op/preferences/forms.py @@ -29,6 +29,7 @@ each. from django import forms from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ +from re2o.mixins import AutocompleteMultipleModelMixin from .models import MultiopOption @@ -39,3 +40,8 @@ class EditMultiopOptionForm(ModelForm): class Meta: model = MultiopOption fields = "__all__" + widgets = { + "enabled_dorm": AutocompleteMultipleModelMixin( + url="/topologie/dormitory-autocomplete", + ), + } From 303531fed368b1b278fe929f62e2bd7aeb250dd9 Mon Sep 17 00:00:00 2001 From: chirac Date: Thu, 31 Dec 2020 14:55:10 +0100 Subject: [PATCH 429/490] Add custom can_list acl for unpriviged views --- machines/models.py | 56 +++++++++++++++++++++++++++++++++++++++++++++ topologie/models.py | 48 ++++++++++++++++++++++++++++++++++++++ users/models.py | 32 ++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) diff --git a/machines/models.py b/machines/models.py index 309e0dd4..711b9b79 100644 --- a/machines/models.py +++ b/machines/models.py @@ -378,6 +378,34 @@ class MachineType(RevMixin, AclMixin, models.Model): ) return True, None, None + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list unprivileged machinetypes + Only members of privileged groups can list all. + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + can, _message, _group = cls.can_use_all(user_request) + if can: + return ( + True, + None, + None, + cls.objects.all() + ) + else: + return ( + False, + _("You don't have the right to use all machine types."), + ("machines.use_all_machinetype",), + cls.objects.filter( + ip_type__in=IpType.objects.filter(need_infra=False) + ), + ) + def __str__(self): return self.name @@ -2130,6 +2158,34 @@ class IpList(RevMixin, AclMixin, models.Model): self.clean() super(IpList, self).save(*args, **kwargs) + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """Only privilged users can list all ipv4. + Others can list Ipv4 related with unprivileged type. + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + can, _message, _group = IpType.can_use_all(user_request) + if can: + return ( + True, + None, + None, + cls.objects.all() + ) + else: + return ( + False, + _("You don't have the right to use all machine types."), + ("machines.use_all_machinetype",), + cls.objects.filter( + ip_type__in=IpType.objects.filter(need_infra=False) + ), + ) + def __str__(self): return self.ipv4 diff --git a/topologie/models.py b/topologie/models.py index c0362480..b7219d27 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -731,6 +731,22 @@ class Dormitory(AclMixin, RevMixin, models.Model): else: return cache.get_or_set("multiple_dorms", cls.objects.count() > 1) + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list dormitory + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + return ( + True, + None, + None, + cls.objects.all() + ) + def __str__(self): return self.name @@ -762,6 +778,22 @@ class Building(AclMixin, RevMixin, models.Model): else: return self.name + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list building + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + return ( + True, + None, + None, + cls.objects.all() + ) + @cached_property def cached_name(self): return self.get_name() @@ -944,6 +976,22 @@ class Room(AclMixin, RevMixin, models.Model): verbose_name_plural = _("rooms") unique_together = ("name", "building") + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list room + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + return ( + True, + None, + None, + cls.objects.all() + ) + def __str__(self): return self.building.cached_name + " " + self.name diff --git a/users/models.py b/users/models.py index 14215fdf..fc0c711a 100755 --- a/users/models.py +++ b/users/models.py @@ -2364,6 +2364,22 @@ class School(RevMixin, AclMixin, models.Model): verbose_name = _("school") verbose_name_plural = _("schools") + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list schools + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + return ( + True, + None, + None, + cls.objects.all() + ) + def __str__(self): return self.name @@ -2487,6 +2503,22 @@ class ListShell(RevMixin, AclMixin, models.Model): """ return self.shell.split("/")[-1] + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list shells + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + return ( + True, + None, + None, + cls.objects.all() + ) + def __str__(self): return self.shell From 331a75d77a061eee9acf8385ac10bf52c5e1e786 Mon Sep 17 00:00:00 2001 From: chirac Date: Thu, 31 Dec 2020 15:31:49 +0100 Subject: [PATCH 430/490] Add club manager acl for user search --- re2o/views.py | 5 ++++- users/models.py | 27 +++++++++++++++++++++++++++ users/views_autocomplete.py | 15 +++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/re2o/views.py b/re2o/views.py index 359c3e81..a689aab8 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -184,8 +184,11 @@ class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView def get_queryset(self): can, reason, _permission, query_set = self.obj_type.can_list(self.request.user) + if query_set: + self.query_set = query_set + else: + self.query_set = self.obj_type.objects.none() - self.query_set = query_set if hasattr(self, "filter_results"): self.filter_results() else: diff --git a/users/models.py b/users/models.py index fc0c711a..9fb10972 100755 --- a/users/models.py +++ b/users/models.py @@ -2065,6 +2065,33 @@ class Adherent(User): ("users.add_user",), ) + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """Users can list adherent only if they are : + - Members of view acl, + - Club administrator. + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + can, _message, _group = Club.can_view_all(user_request) + if user_request.has_perm("users.view_user") or can: + return ( + True, + None, + None, + cls.objects.all() + ) + else: + return ( + False, + _("You don't have the right to list all adherents."), + ("users.view_user",), + cls.objects.none(), + ) + def clean(self, *args, **kwargs): """Method, clean and validate the gpgfp value. diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index f0cfc3ae..bc34a582 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -81,6 +81,21 @@ class UserAutocomplete(AutocompleteViewMixin): class AdherentAutocomplete(AutocompleteViewMixin): obj_type = Adherent + # Precision on search to add annotations so search behaves more like users expect it to + def filter_results(self): + # Comments explain what we try to match + self.query_set = self.query_set.annotate( + full_name=Concat("name", Value(" "), "surname"), # Match when the user searches "Toto Passoir" + full_name_reverse=Concat("surname", Value(" "), "name"), # Match when the user searches "Passoir Toto" + ).all() + + if self.q: + self.query_set = self.query_set.filter( + Q(pseudo__icontains=self.q) + | Q(full_name__icontains=self.q) + | Q(full_name_reverse__icontains=self.q) + ) + #@can_view_all(Club) class ClubAutocomplete(AutocompleteViewMixin): obj_type = Club From 89cd0ac85658dc464e41b71ba350e824ae6a674f Mon Sep 17 00:00:00 2001 From: chirac Date: Thu, 31 Dec 2020 19:42:08 +0100 Subject: [PATCH 431/490] Remove useless imports --- machines/views_autocomplete.py | 4 ---- topologie/views_autocomplete.py | 7 ------- users/views_autocomplete.py | 14 -------------- 3 files changed, 25 deletions(-) diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index ef259da3..6d3b7d71 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -48,10 +48,6 @@ from .models import ( from re2o.views import AutocompleteViewMixin -from re2o.acl import ( - can_view_all, -) - class VlanAutocomplete(AutocompleteViewMixin): obj_type = Vlan diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index 7f8553c1..eb3e7f2b 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -46,12 +46,7 @@ from .models import ( from re2o.views import AutocompleteViewMixin -from re2o.acl import ( - can_view_all, -) - -#@can_view_all(School) class RoomAutocomplete(AutocompleteViewMixin): obj_type = Room @@ -77,12 +72,10 @@ class RoomAutocomplete(AutocompleteViewMixin): ) -#@can_view_all(Dormitory) class DormitoryAutocomplete(AutocompleteViewMixin): obj_type = Dormitory -#@can_view_all(Building) class BuildingAutocomplete(AutocompleteViewMixin): obj_type = Building diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index bc34a582..68ad58ad 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -33,32 +33,20 @@ from __future__ import unicode_literals from .models import ( User, - Ban, - Whitelist, School, - ListRight, - Request, - ServiceUser, Adherent, Club, ListShell, - EMailAddress, ) from re2o.views import AutocompleteViewMixin -from re2o.acl import ( - can_view_all, -) - from django.db.models import Q, Value, CharField from django.db.models.functions import Concat -#@can_view_all(School) class SchoolAutocomplete(AutocompleteViewMixin): obj_type = School -#@can_view_all(User) class UserAutocomplete(AutocompleteViewMixin): obj_type = User @@ -77,7 +65,6 @@ class UserAutocomplete(AutocompleteViewMixin): | Q(full_name_reverse__icontains=self.q) ) -#can_view_all(Adherent) class AdherentAutocomplete(AutocompleteViewMixin): obj_type = Adherent @@ -96,7 +83,6 @@ class AdherentAutocomplete(AutocompleteViewMixin): | Q(full_name_reverse__icontains=self.q) ) -#@can_view_all(Club) class ClubAutocomplete(AutocompleteViewMixin): obj_type = Club From 0d575366383c60c4638555ecc8b381938140400e Mon Sep 17 00:00:00 2001 From: chirac Date: Thu, 31 Dec 2020 19:50:41 +0100 Subject: [PATCH 432/490] Correct customs acl list for extensions --- machines/models.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/machines/models.py b/machines/models.py index 711b9b79..cb1f835b 100644 --- a/machines/models.py +++ b/machines/models.py @@ -981,6 +981,32 @@ class Extension(RevMixin, AclMixin, models.Model): ("machines.use_all_extension",), ) + @classmethod + def can_list(cls, user_request, *_args, **_kwargs): + """All users can list unprivileged extensions + Only members of privileged groups can list all. + + :param user_request: The user who wants to view the list. + :return: True if the user can view the list and an explanation + message. + + """ + can, _message, _group = cls.can_use_all(user_request) + if can: + return ( + True, + None, + None, + cls.objects.all() + ) + else: + return ( + False, + _("You don't have the right to list all extensions."), + ("machines.use_all_extension",), + cls.objects.filter(need_infra=False), + ) + def __str__(self): return self.name From 4c984a3975abf9f26722969b66abad050acc4c5a Mon Sep 17 00:00:00 2001 From: chirac Date: Thu, 31 Dec 2020 20:12:36 +0100 Subject: [PATCH 433/490] Black on files --- cotisations/forms.py | 14 ++-- logs/forms.py | 22 ++---- machines/forms.py | 91 ++++++++--------------- machines/views_autocomplete.py | 19 +++-- re2o/mixins.py | 16 ++-- re2o/views.py | 2 +- topologie/forms.py | 44 +++++------ topologie/views_autocomplete.py | 61 ++++++++++----- users/forms.py | 128 +++++++++++++++----------------- users/views_autocomplete.py | 29 +++++--- 10 files changed, 204 insertions(+), 222 deletions(-) diff --git a/cotisations/forms.py b/cotisations/forms.py index 926db818..4689e3cb 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -45,7 +45,11 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin +from re2o.mixins import ( + FormRevMixin, + AutocompleteModelMixin, + AutocompleteMultipleModelMixin, +) from .models import ( Article, Paiement, @@ -80,12 +84,8 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): model = Facture fields = "__all__" widgets = { - "user": AutocompleteModelMixin( - url="/users/user-autocomplete", - ), - "banque": AutocompleteModelMixin( - url="/cotisations/banque-autocomplete", - ), + "user": AutocompleteModelMixin(url="/users/user-autocomplete"), + "banque": AutocompleteModelMixin(url="/cotisations/banque-autocomplete"), } def clean(self): diff --git a/logs/forms.py b/logs/forms.py index eb623827..03045e4a 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -47,10 +47,7 @@ CHOICES_ACTION_TYPE = ( ("all", _("All")), ) -CHOICES_TYPE = ( - ("ip", _("IPv4")), - ("mac", _("MAC address")), -) +CHOICES_TYPE = (("ip", _("IPv4")), ("mac", _("MAC address"))) def all_classes(module): @@ -88,14 +85,11 @@ def classes_for_action_type(action_type): users.models.User.__name__, users.models.Adherent.__name__, users.models.Club.__name__, - users.models.EMailAddress.__name__ + users.models.EMailAddress.__name__, ] if action_type == "machines": - return [ - machines.models.Machine.__name__, - machines.models.Interface.__name__ - ] + return [machines.models.Machine.__name__, machines.models.Interface.__name__] if action_type == "subscriptions": return all_classes(cotisations.models) @@ -115,6 +109,7 @@ def classes_for_action_type(action_type): class ActionsSearchForm(Form): """Form used to do an advanced search through the logs.""" + user = forms.ModelChoiceField( label=_("Performed by"), queryset=users.models.User.objects.all(), @@ -143,13 +138,10 @@ class ActionsSearchForm(Form): class MachineHistorySearchForm(Form): """Form used to do a search through the machine histories.""" - q = forms.CharField( - label=_("Search"), - max_length=100, - ) + + q = forms.CharField(label=_("Search"), max_length=100) t = forms.CharField( - label=_("Search type"), - widget=forms.Select(choices=CHOICES_TYPE) + label=_("Search type"), widget=forms.Select(choices=CHOICES_TYPE) ) s = forms.DateField(required=False, label=_("Start date")) e = forms.DateField(required=False, label=_("End date")) diff --git a/machines/forms.py b/machines/forms.py index 3f6a3864..9a563d9a 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -40,7 +40,11 @@ from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin +from re2o.mixins import ( + FormRevMixin, + AutocompleteModelMixin, + AutocompleteMultipleModelMixin, +) from .models import ( Domain, Machine, @@ -71,11 +75,7 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Machine fields = "__all__" - widgets = { - "user": AutocompleteModelMixin( - url="/users/user-autocomplete", - ), - } + widgets = {"user": AutocompleteModelMixin(url="/users/user-autocomplete")} def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -97,17 +97,16 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): model = Interface fields = ["machine", "machine_type", "ipv4", "mac_address", "details"] widgets = { - "machine": AutocompleteModelMixin( - url="/machines/machine-autocomplete", - ), + "machine": AutocompleteModelMixin(url="/machines/machine-autocomplete"), "machine_type": AutocompleteModelMixin( - url="/machines/machinetype-autocomplete", + url="/machines/machinetype-autocomplete" ), "ipv4": AutocompleteModelMixin( - url="/machines/iplist-autocomplete", forward=['machine_type'], + url="/machines/iplist-autocomplete", + forward=["machine_type"], attrs={ - 'data-placeholder': 'Automatic assigment. Type to choose specific ip.', - } + "data-placeholder": "Automatic assigment. Type to choose specific ip." + }, ), } @@ -159,9 +158,7 @@ class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): model = Domain fields = ["name", "extension", "ttl"] widgets = { - "extension": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), + "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -213,9 +210,7 @@ class MachineTypeForm(FormRevMixin, ModelForm): model = MachineType fields = ["name", "ip_type"] widgets = { - "ip_type": AutocompleteModelMixin( - url="/machines/iptype-autocomplete", - ), + "ip_type": AutocompleteModelMixin(url="/machines/iptype-autocomplete") } def __init__(self, *args, **kwargs): @@ -252,14 +247,10 @@ class IpTypeForm(FormRevMixin, ModelForm): model = IpType fields = "__all__" widgets = { - "vlan": AutocompleteModelMixin( - url="/machines/vlan-autocomplete", - ), - "extension": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), + "vlan": AutocompleteModelMixin(url="/machines/vlan-autocomplete"), + "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete"), "ouverture_ports": AutocompleteModelMixin( - url="/machines/ouvertureportlist-autocomplete", + url="/machines/ouvertureportlist-autocomplete" ), } @@ -392,12 +383,8 @@ class MxForm(FormRevMixin, ModelForm): model = Mx fields = ["zone", "priority", "name", "ttl"] widgets = { - "zone": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), - "name": AutocompleteModelMixin( - url="/machines/domain-autocomplete", - ), + "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete"), + "name": AutocompleteModelMixin(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -435,12 +422,8 @@ class NsForm(FormRevMixin, ModelForm): model = Ns fields = ["zone", "ns", "ttl"] widgets = { - "zone": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), - "ns": AutocompleteModelMixin( - url="/machines/domain-autocomplete", - ), + "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete"), + "ns": AutocompleteModelMixin(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -476,9 +459,7 @@ class TxtForm(FormRevMixin, ModelForm): model = Txt fields = "__all__" widgets = { - "zone": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), + "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -511,9 +492,7 @@ class DNameForm(FormRevMixin, ModelForm): model = DName fields = "__all__" widgets = { - "zone": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), + "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -546,12 +525,8 @@ class SrvForm(FormRevMixin, ModelForm): model = Srv fields = "__all__" widgets = { - "extension": AutocompleteModelMixin( - url="/machines/extension-autocomplete", - ), - "target": AutocompleteModelMixin( - url="/machines/domain-autocomplete", - ), + "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete"), + "target": AutocompleteModelMixin(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -585,10 +560,10 @@ class NasForm(FormRevMixin, ModelForm): fields = "__all__" widgets = { "nas_type": AutocompleteModelMixin( - url="/machines/machinetype-autocomplete", + url="/machines/machinetype-autocomplete" ), "machine_type": AutocompleteModelMixin( - url="/machines/machinetype-autocomplete", + url="/machines/machinetype-autocomplete" ), } @@ -623,8 +598,8 @@ class RoleForm(FormRevMixin, ModelForm): fields = "__all__" widgets = { "servers": AutocompleteMultipleModelMixin( - url="/machines/interface-autocomplete", - ), + url="/machines/interface-autocomplete" + ) } def __init__(self, *args, **kwargs): @@ -661,8 +636,8 @@ class ServiceForm(FormRevMixin, ModelForm): fields = "__all__" widgets = { "servers": AutocompleteMultipleModelMixin( - url="/machines/interface-autocomplete", - ), + url="/machines/interface-autocomplete" + ) } def __init__(self, *args, **kwargs): @@ -750,8 +725,8 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm): fields = ["port_lists"] widgets = { "port_lists": AutocompleteMultipleModelMixin( - url="/machines/ouvertureportlist-autocomplete", - ), + url="/machines/ouvertureportlist-autocomplete" + ) } def __init__(self, *args, **kwargs): diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index 6d3b7d71..076d13a2 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -43,7 +43,7 @@ from .models import ( Extension, Domain, OuverturePortList, - IpList + IpList, ) from re2o.views import AutocompleteViewMixin @@ -66,15 +66,15 @@ class IpTypeAutocomplete(AutocompleteViewMixin): class ExtensionAutocomplete(AutocompleteViewMixin): - obj_type = Extension + obj_type = Extension class DomainAutocomplete(AutocompleteViewMixin): - obj_type = Domain + obj_type = Domain class OuverturePortListAutocomplete(AutocompleteViewMixin): - obj_type = OuverturePortList + obj_type = OuverturePortList class InterfaceAutocomplete(AutocompleteViewMixin): @@ -84,8 +84,7 @@ class InterfaceAutocomplete(AutocompleteViewMixin): def filter_results(self): if self.q: self.query_set = self.query_set.filter( - Q(domain__name__icontains=self.q) - | Q(machine__name__icontains=self.q) + Q(domain__name__icontains=self.q) | Q(machine__name__icontains=self.q) ) @@ -94,11 +93,11 @@ class IpListAutocomplete(AutocompleteViewMixin): # Precision on search to add annotations so search behaves more like users expect it to def filter_results(self): - machine_type = self.forwarded.get('machine_type', None) + machine_type = self.forwarded.get("machine_type", None) self.query_set = self.query_set.filter(interface__isnull=True) if machine_type: - self.query_set = self.query_set.filter(ip_type__machinetype__id=machine_type) - if self.q: self.query_set = self.query_set.filter( - Q(ipv4__startswith=self.q) + ip_type__machinetype__id=machine_type ) + if self.q: + self.query_set = self.query_set.filter(Q(ipv4__startswith=self.q)) diff --git a/re2o/mixins.py b/re2o/mixins.py index df5d7310..9aa39081 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -29,7 +29,6 @@ from django.utils.translation import ugettext as _ from dal import autocomplete - class RevMixin(object): """A mixin to subclass the save and delete function of a model to enforce the versioning of the object before those actions @@ -260,6 +259,7 @@ class AutocompleteModelMixin(autocomplete.ModelSelect2): """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 """ + def __init__(self, *args, **kwargs): select2_attrs = kwargs.get("attrs", {}) kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) @@ -271,9 +271,11 @@ class AutocompleteModelMixin(autocomplete.ModelSelect2): See https://select2.org/configuration/options-api """ # By default, only trigger autocompletion after 3 characters have been typed - #attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) # If there are less than 10 results, just show all of them (no need to autocomplete) - attrs["data-minimum-results-for-search"] = attrs.get("data-minimum-results-for-search", 10) + attrs["data-minimum-results-for-search"] = attrs.get( + "data-minimum-results-for-search", 10 + ) return attrs @@ -281,6 +283,7 @@ class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 """ + def __init__(self, *args, **kwargs): select2_attrs = kwargs.get("attrs", {}) kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) @@ -292,8 +295,9 @@ class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): See https://select2.org/configuration/options-api """ # By default, only trigger autocompletion after 3 characters have been typed - #attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) # If there are less than 10 results, just show all of them (no need to autocomplete) - attrs["data-minimum-results-for-search"] = attrs.get("data-minimum-results-for-search", 10) + attrs["data-minimum-results-for-search"] = attrs.get( + "data-minimum-results-for-search", 10 + ) return attrs - diff --git a/re2o/views.py b/re2o/views.py index a689aab8..ed7d2cff 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -193,5 +193,5 @@ class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView self.filter_results() else: if self.q: - self.query_set = self.query_set.filter(**{ self.query_filter: self.q}) + self.query_set = self.query_set.filter(**{self.query_filter: self.q}) return self.query_set diff --git a/topologie/forms.py b/topologie/forms.py index fc7a4106..e88218fd 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -37,7 +37,11 @@ from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import EditMachineForm, NewMachineForm -from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin +from re2o.mixins import ( + FormRevMixin, + AutocompleteModelMixin, + AutocompleteMultipleModelMixin, +) from .models import ( Port, @@ -63,20 +67,14 @@ class PortForm(FormRevMixin, ModelForm): model = Port fields = "__all__" widgets = { - "switch": AutocompleteModelMixin( - url="/topologie/switch-autocomplete", - ), - "room": AutocompleteModelMixin( - url="/topologie/room-autocomplete", - ), + "switch": AutocompleteModelMixin(url="/topologie/switch-autocomplete"), + "room": AutocompleteModelMixin(url="/topologie/room-autocomplete"), "machine_interface": AutocompleteModelMixin( - url="/machine/machine-autocomplete", - ), - "related": AutocompleteModelMixin( - url="/topologie/port-autocomplete", + url="/machine/machine-autocomplete" ), + "related": AutocompleteModelMixin(url="/topologie/port-autocomplete"), "custom_profile": AutocompleteModelMixin( - url="/topologie/portprofile-autocomplete", + url="/topologie/portprofile-autocomplete" ), } @@ -184,11 +182,9 @@ class EditSwitchForm(EditMachineForm): fields = "__all__" widgets = { "switchbay": AutocompleteModelMixin( - url="/topologie/switchbay-autocomplete", - ), - "user": AutocompleteModelMixin( - url="/users/user-autocomplete", + url="/topologie/switchbay-autocomplete" ), + "user": AutocompleteModelMixin(url="/users/user-autocomplete"), } @@ -206,9 +202,7 @@ class EditRoomForm(FormRevMixin, ModelForm): model = Room fields = "__all__" widgets = { - "building": AutocompleteModelMixin( - url="/topologie/building-autocomplete", - ), + "building": AutocompleteModelMixin(url="/topologie/building-autocomplete") } def __init__(self, *args, **kwargs): @@ -229,7 +223,7 @@ class EditModelSwitchForm(FormRevMixin, ModelForm): members = forms.ModelMultipleChoiceField( Switch.objects.all(), widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), - required=False + required=False, ) class Meta: @@ -274,9 +268,7 @@ class EditSwitchBayForm(FormRevMixin, ModelForm): model = SwitchBay fields = "__all__" widgets = { - "building": AutocompleteModelMixin( - url="/topologie/building-autocomplete", - ), + "building": AutocompleteModelMixin(url="/topologie/building-autocomplete") } def __init__(self, *args, **kwargs): @@ -324,11 +316,9 @@ class EditPortProfileForm(FormRevMixin, ModelForm): fields = "__all__" widgets = { "vlan_tagged": AutocompleteMultipleModelMixin( - url="/machines/vlan-autocomplete", - ), - "vlan_untagged": AutocompleteModelMixin( - url="/machines/vlan-autocomplete", + url="/machines/vlan-autocomplete" ), + "vlan_untagged": AutocompleteModelMixin(url="/machines/vlan-autocomplete"), } def __init__(self, *args, **kwargs): diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index eb3e7f2b..e5c6ee6d 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -34,15 +34,7 @@ from __future__ import unicode_literals from django.db.models import Q, Value, CharField from django.db.models.functions import Concat -from .models import ( - Room, - Dormitory, - Building, - Switch, - PortProfile, - Port, - SwitchBay, -) +from .models import Room, Dormitory, Building, Switch, PortProfile, Port, SwitchBay from re2o.views import AutocompleteViewMixin @@ -55,11 +47,27 @@ class RoomAutocomplete(AutocompleteViewMixin): # Suppose we have a dorm named Dorm, a building name B, and rooms from 001 - 999 # Comments explain what we try to match self.query_set = self.query_set.annotate( - full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "B 001" + full_name=Concat( + "building__name", Value(" "), "name" + ), # Match when the user searches "B 001" full_name_stuck=Concat("building__name", "name"), # Match "B001" - dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm 001" - dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm B 001" - dorm_full_colon_name=Concat("building__dormitory__name", Value(" : "), "building__name", Value(" "), "name"), # Match "Dorm : B 001" (see Room's full_name property) + dorm_name=Concat( + "building__dormitory__name", Value(" "), "name" + ), # Match "Dorm 001" + dorm_full_name=Concat( + "building__dormitory__name", + Value(" "), + "building__name", + Value(" "), + "name", + ), # Match "Dorm B 001" + dorm_full_colon_name=Concat( + "building__dormitory__name", + Value(" : "), + "building__name", + Value(" "), + "name", + ), # Match "Dorm : B 001" (see Room's full_name property) ).all() if self.q: @@ -89,8 +97,7 @@ class BuildingAutocomplete(AutocompleteViewMixin): if self.q: self.query_set = self.query_set.filter( - Q(full_name__icontains=self.q) - | Q(full_name_colon__icontains=self.q) + Q(full_name__icontains=self.q) | Q(full_name_colon__icontains=self.q) ) @@ -106,9 +113,13 @@ class PortAutocomplete(AutocompleteViewMixin): # We want to enter the switch name, not just the port number # Because we're concatenating a CharField and an Integer, we have to sepcify the output_field self.query_set = self.query_set.annotate( - full_name=Concat("switch__name", Value(" "), "port", output_field=CharField()), + full_name=Concat( + "switch__name", Value(" "), "port", output_field=CharField() + ), full_name_stuck=Concat("switch__name", "port", output_field=CharField()), - full_name_dash=Concat("switch__name", Value(" - "), "port", output_field=CharField()), + full_name_dash=Concat( + "switch__name", Value(" - "), "port", output_field=CharField() + ), ).all() if self.q: @@ -126,9 +137,19 @@ class SwitchBayAutocomplete(AutocompleteViewMixin): def filter_results(self): # Comments explain what we try to match self.query_set = self.query_set.annotate( - full_name=Concat("building__name", Value(" "), "name"), # Match when the user searches "" - dorm_name=Concat("building__dormitory__name", Value(" "), "name"), # Match "Dorm Local Sud" - dorm_full_name=Concat("building__dormitory__name", Value(" "), "building__name", Value(" "), "name"), # Match "Dorm J Local Sud" + full_name=Concat( + "building__name", Value(" "), "name" + ), # Match when the user searches "" + dorm_name=Concat( + "building__dormitory__name", Value(" "), "name" + ), # Match "Dorm Local Sud" + dorm_full_name=Concat( + "building__dormitory__name", + Value(" "), + "building__name", + Value(" "), + "name", + ), # Match "Dorm J Local Sud" ).all() if self.q: diff --git a/users/forms.py b/users/forms.py index 195807a3..dbe1cf7e 100644 --- a/users/forms.py +++ b/users/forms.py @@ -46,7 +46,10 @@ from os import walk, path from django import forms from django.forms import ModelForm, Form from django.contrib.auth.forms import ReadOnlyPasswordHashField -from django.contrib.auth.password_validation import validate_password, password_validators_help_text_html +from django.contrib.auth.password_validation import ( + validate_password, + password_validators_help_text_html, +) from django.core.validators import MinLengthValidator from django.conf import settings from django.utils import timezone @@ -60,7 +63,11 @@ from topologie.models import Port from preferences.models import OptionalUser from re2o.utils import remove_user_room from re2o.base import get_input_formats_help_text -from re2o.mixins import FormRevMixin, AutocompleteMultipleModelMixin, AutocompleteModelMixin +from re2o.mixins import ( + FormRevMixin, + AutocompleteMultipleModelMixin, + AutocompleteModelMixin, +) from re2o.field_permissions import FieldPermissionFormMixin from preferences.models import GeneralOption @@ -156,14 +163,10 @@ class ServiceUserAdminForm(FormRevMixin, forms.ModelForm): """ password1 = forms.CharField( - label=_("Password"), - widget=forms.PasswordInput, - max_length=255, + label=_("Password"), widget=forms.PasswordInput, max_length=255 ) password2 = forms.CharField( - label=_("Password confirmation"), - widget=forms.PasswordInput, - max_length=255, + label=_("Password confirmation"), widget=forms.PasswordInput, max_length=255 ) def __init__(self, *args, **kwargs): @@ -215,6 +218,7 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): DjangoForm : Inherit from basic django form """ + selfpasswd = forms.CharField( label=_("Current password"), max_length=255, widget=forms.PasswordInput ) @@ -222,12 +226,10 @@ class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm): label=_("New password"), max_length=255, widget=forms.PasswordInput, - help_text=password_validators_help_text_html() + help_text=password_validators_help_text_html(), ) passwd2 = forms.CharField( - label=_("New password confirmation"), - max_length=255, - widget=forms.PasswordInput, + label=_("New password confirmation"), max_length=255, widget=forms.PasswordInput ) class Meta: @@ -280,7 +282,7 @@ class ResetPasswordForm(forms.Form): Parameters: DjangoForm : Inherit from basic django form - """ + """ pseudo = forms.CharField(label=_("Username"), max_length=255) email = forms.EmailField(max_length=255) @@ -292,13 +294,11 @@ class MassArchiveForm(forms.Form): Parameters: DjangoForm : Inherit from basic django form - """ + """ date = forms.DateTimeField(help_text="%d/%m/%y") full_archive = forms.BooleanField( - label=_( - "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" - ), + label=_("Fully archive users? WARNING: CRITICAL OPERATION IF TRUE"), initial=False, required=False, ) @@ -323,7 +323,7 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -351,18 +351,14 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "room", ] widgets = { - "school": AutocompleteModelMixin( - url="/users/school-autocomplete", - ), + "school": AutocompleteModelMixin(url="/users/school-autocomplete"), "room": AutocompleteModelMixin( url="/topologie/room-autocomplete", - attrs = { - "data-minimum-input-length": 3 # Only trigger autocompletion after 3 characters have been typed - } + attrs={ + "data-minimum-input-length": 3 # Only trigger autocompletion after 3 characters have been typed + }, ), - "shell": AutocompleteModelMixin( - url="/users/shell-autocomplete", - ) + "shell": AutocompleteModelMixin(url="/users/shell-autocomplete"), } force = forms.BooleanField( @@ -426,7 +422,8 @@ class AdherentCreationForm(AdherentForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ + # Champ pour choisir si un lien est envoyé par mail pour le mot de passe init_password_by_mail_info = _( "If this options is set, you will receive a link to set" @@ -439,9 +436,7 @@ class AdherentCreationForm(AdherentForm): ) init_password_by_mail = forms.BooleanField( - help_text=init_password_by_mail_info, - required=False, - initial=True + help_text=init_password_by_mail_info, required=False, initial=True ) init_password_by_mail.label = _("Send password reset link by email.") @@ -452,7 +447,7 @@ class AdherentCreationForm(AdherentForm): label=_("Password"), widget=forms.PasswordInput, max_length=255, - help_text=password_validators_help_text_html() + help_text=password_validators_help_text_html(), ) password2 = forms.CharField( required=False, @@ -542,8 +537,12 @@ class AdherentCreationForm(AdherentForm): # Save the provided password in hashed format user = super(AdherentForm, self).save(commit=False) - is_set_password_allowed = OptionalUser.get_cached_value("allow_set_password_during_user_creation") - set_passwd = is_set_password_allowed and not self.cleaned_data.get("init_password_by_mail") + is_set_password_allowed = OptionalUser.get_cached_value( + "allow_set_password_during_user_creation" + ) + set_passwd = is_set_password_allowed and not self.cleaned_data.get( + "init_password_by_mail" + ) if set_passwd: user.set_password(self.cleaned_data["password1"]) @@ -558,7 +557,7 @@ class AdherentEditForm(AdherentForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): super(AdherentEditForm, self).__init__(*args, **kwargs) @@ -594,7 +593,7 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -624,15 +623,9 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "mailing", ] widgets = { - "school": AutocompleteModelMixin( - url="/users/school-autocomplete", - ), - "room": AutocompleteModelMixin( - url="/topologie/room-autocomplete", - ), - "shell": AutocompleteModelMixin( - url="/users/shell-autocomplete", - ) + "school": AutocompleteModelMixin(url="/users/school-autocomplete"), + "room": AutocompleteModelMixin(url="/topologie/room-autocomplete"), + "shell": AutocompleteModelMixin(url="/users/shell-autocomplete"), } def clean_telephone(self): @@ -657,18 +650,18 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta: model = Club fields = ["administrators", "members"] widgets = { "administrators": AutocompleteMultipleModelMixin( - url="/users/adherent-autocomplete", + url="/users/adherent-autocomplete" ), "members": AutocompleteMultipleModelMixin( - url="/users/adherent-autocomplete", - ) + url="/users/adherent-autocomplete" + ), } def __init__(self, *args, **kwargs): @@ -681,7 +674,7 @@ class PasswordForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta: model = User @@ -698,7 +691,7 @@ class ServiceUserForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ password = forms.CharField( label=_("New password"), @@ -737,7 +730,7 @@ class EditServiceUserForm(ServiceUserForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ password = forms.CharField( label=_("New password"), @@ -757,7 +750,7 @@ class StateForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta: model = User @@ -775,7 +768,7 @@ class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ groups = forms.ModelMultipleChoiceField( Group.objects.all(), widget=forms.CheckboxSelectMultiple, required=False @@ -797,7 +790,7 @@ class SchoolForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta: model = School @@ -814,7 +807,7 @@ class ShellForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta: model = ListShell @@ -833,7 +826,7 @@ class ListRightForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ permissions = forms.ModelMultipleChoiceField( Permission.objects.all().select_related("content_type"), @@ -857,7 +850,7 @@ class NewListRightForm(ListRightForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ class Meta(ListRightForm.Meta): fields = ("name", "unix_name", "gid", "critical", "permissions", "details") @@ -875,7 +868,7 @@ class DelListRightForm(Form): Parameters: DjangoForm : Inherit from basic django form - """ + """ listrights = forms.ModelMultipleChoiceField( queryset=ListRight.objects.none(), @@ -898,7 +891,7 @@ class DelSchoolForm(Form): Parameters: DjangoForm : Inherit from basic django form - """ + """ schools = forms.ModelMultipleChoiceField( queryset=School.objects.none(), @@ -920,7 +913,7 @@ class BanForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -939,7 +932,7 @@ class WhitelistForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -959,7 +952,7 @@ class EMailAddressForm(FormRevMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -980,7 +973,7 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): Parameters: DjangoForm : Inherit from basic django form - """ + """ def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -1004,7 +997,8 @@ class InitialRegisterForm(forms.Form): Parameters: DjangoForm : Inherit from basic django form - """ + """ + register_room = forms.BooleanField(required=False) register_machine = forms.BooleanField(required=False) @@ -1085,8 +1079,8 @@ class ThemeForm(FormRevMixin, forms.Form): theme = forms.ChoiceField(widget=forms.Select()) def __init__(self, *args, **kwargs): - _, _ ,themes = next(walk(path.join(settings.STATIC_ROOT, "css/themes"))) + _, _, themes = next(walk(path.join(settings.STATIC_ROOT, "css/themes"))) if not themes: themes = ["default.css"] super(ThemeForm, self).__init__(*args, **kwargs) - self.fields['theme'].choices = [(theme, theme) for theme in themes] + self.fields["theme"].choices = [(theme, theme) for theme in themes] diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index 68ad58ad..e202ec4d 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -31,22 +31,18 @@ Here are defined the autocomplete class based view. """ from __future__ import unicode_literals -from .models import ( - User, - School, - Adherent, - Club, - ListShell, -) +from .models import User, School, Adherent, Club, ListShell from re2o.views import AutocompleteViewMixin from django.db.models import Q, Value, CharField from django.db.models.functions import Concat + class SchoolAutocomplete(AutocompleteViewMixin): obj_type = School + class UserAutocomplete(AutocompleteViewMixin): obj_type = User @@ -54,8 +50,12 @@ class UserAutocomplete(AutocompleteViewMixin): def filter_results(self): # Comments explain what we try to match self.query_set = self.query_set.annotate( - full_name=Concat("adherent__name", Value(" "), "surname"), # Match when the user searches "Toto Passoir" - full_name_reverse=Concat("surname", Value(" "), "adherent__name"), # Match when the user searches "Passoir Toto" + full_name=Concat( + "adherent__name", Value(" "), "surname" + ), # Match when the user searches "Toto Passoir" + full_name_reverse=Concat( + "surname", Value(" "), "adherent__name" + ), # Match when the user searches "Passoir Toto" ).all() if self.q: @@ -65,6 +65,7 @@ class UserAutocomplete(AutocompleteViewMixin): | Q(full_name_reverse__icontains=self.q) ) + class AdherentAutocomplete(AutocompleteViewMixin): obj_type = Adherent @@ -72,8 +73,12 @@ class AdherentAutocomplete(AutocompleteViewMixin): def filter_results(self): # Comments explain what we try to match self.query_set = self.query_set.annotate( - full_name=Concat("name", Value(" "), "surname"), # Match when the user searches "Toto Passoir" - full_name_reverse=Concat("surname", Value(" "), "name"), # Match when the user searches "Passoir Toto" + full_name=Concat( + "name", Value(" "), "surname" + ), # Match when the user searches "Toto Passoir" + full_name_reverse=Concat( + "surname", Value(" "), "name" + ), # Match when the user searches "Passoir Toto" ).all() if self.q: @@ -83,9 +88,11 @@ class AdherentAutocomplete(AutocompleteViewMixin): | Q(full_name_reverse__icontains=self.q) ) + class ClubAutocomplete(AutocompleteViewMixin): obj_type = Club + class ShellAutocomplete(AutocompleteViewMixin): obj_type = ListShell query_filter = "shell__icontains" From ce87d7c3de7e5abea56f58176c9d071fffa03e10 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 1 Jan 2021 20:28:44 +0100 Subject: [PATCH 434/490] Remove unusefull dependency --- pip_requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/pip_requirements.txt b/pip_requirements.txt index 148a3b5b..b067c4c5 100644 --- a/pip_requirements.txt +++ b/pip_requirements.txt @@ -1,4 +1,3 @@ django-bootstrap3==11.1.0 django-macaddress==1.6.0 -#django-autocomplete-light==3.2.10 # Until Django 2.0+ django-autocomplete-light From aacb8ef84aa245679dc449fbafed8f69e5056855 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sat, 2 Jan 2021 00:27:31 +0100 Subject: [PATCH 435/490] Fix minor issues with DAL --- machines/views_autocomplete.py | 2 ++ re2o/mixins.py | 8 +++---- re2o/views.py | 3 ++- static/css/autocomplete.css | 2 +- topologie/templates/topologie/topo_more.html | 2 +- topologie/views_autocomplete.py | 25 +++++++++++--------- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/machines/views_autocomplete.py b/machines/views_autocomplete.py index 076d13a2..94b55a59 100644 --- a/machines/views_autocomplete.py +++ b/machines/views_autocomplete.py @@ -95,9 +95,11 @@ class IpListAutocomplete(AutocompleteViewMixin): def filter_results(self): machine_type = self.forwarded.get("machine_type", None) self.query_set = self.query_set.filter(interface__isnull=True) + if machine_type: self.query_set = self.query_set.filter( ip_type__machinetype__id=machine_type ) + if self.q: self.query_set = self.query_set.filter(Q(ipv4__startswith=self.q)) diff --git a/re2o/mixins.py b/re2o/mixins.py index 9aa39081..471f7414 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -270,8 +270,8 @@ class AutocompleteModelMixin(autocomplete.ModelSelect2): """ See https://select2.org/configuration/options-api """ - # By default, only trigger autocompletion after 3 characters have been typed - # attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # Display the "x" button to clear the input by default + attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") # If there are less than 10 results, just show all of them (no need to autocomplete) attrs["data-minimum-results-for-search"] = attrs.get( "data-minimum-results-for-search", 10 @@ -294,8 +294,8 @@ class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): """ See https://select2.org/configuration/options-api """ - # By default, only trigger autocompletion after 3 characters have been typed - # attrs["data-minimum-input-length"] = attrs.get("data-minimum-input-length", 3) + # Display the "x" button to clear the input by default + attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") # If there are less than 10 results, just show all of them (no need to autocomplete) attrs["data-minimum-results-for-search"] = attrs.get( "data-minimum-results-for-search", 10 diff --git a/re2o/views.py b/re2o/views.py index ed7d2cff..aa8ddc4b 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -182,8 +182,8 @@ class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView query_filter = "name__icontains" # Override this if necessary def get_queryset(self): - can, reason, _permission, query_set = self.obj_type.can_list(self.request.user) + if query_set: self.query_set = query_set else: @@ -194,4 +194,5 @@ class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView else: if self.q: self.query_set = self.query_set.filter(**{self.query_filter: self.q}) + return self.query_set diff --git a/static/css/autocomplete.css b/static/css/autocomplete.css index dea68af3..41b355fa 100644 --- a/static/css/autocomplete.css +++ b/static/css/autocomplete.css @@ -39,7 +39,7 @@ See github.com/yourlabs/django-autocomplete-light/issues/1149 .select2-container--default .select2-selection--multiple .select2-selection__rendered { height: 100% !important; - display: inline !imoortant; + display: inline !important; overflow-x: hidden !important; overflow-y: auto !important; } diff --git a/topologie/templates/topologie/topo_more.html b/topologie/templates/topologie/topo_more.html index 74a5265a..be97aa12 100644 --- a/topologie/templates/topologie/topo_more.html +++ b/topologie/templates/topologie/topo_more.html @@ -31,7 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% block content %} {% if topoform %} {% bootstrap_form_errors topoform %} - {{ topoform.media }} +{{ topoform.media }} {% endif %} {% if machineform %} {% bootstrap_form_errors machineform %} diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index e5c6ee6d..e1069b18 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -44,7 +44,7 @@ class RoomAutocomplete(AutocompleteViewMixin): # Precision on search to add annotations so search behaves more like users expect it to def filter_results(self): - # Suppose we have a dorm named Dorm, a building name B, and rooms from 001 - 999 + # Suppose we have a dorm named Dorm, a building named B, and rooms from 001 - 999 # Comments explain what we try to match self.query_set = self.query_set.annotate( full_name=Concat( @@ -87,7 +87,6 @@ class DormitoryAutocomplete(AutocompleteViewMixin): class BuildingAutocomplete(AutocompleteViewMixin): obj_type = Building - # Precision on search to add annotations so search behaves more like users expect it to def filter_results(self): # We want to be able to filter by dorm so it's easier self.query_set = self.query_set.annotate( @@ -108,10 +107,9 @@ class SwitchAutocomplete(AutocompleteViewMixin): class PortAutocomplete(AutocompleteViewMixin): obj_type = Port - # Precision on search to add annotations so search behaves more like users expect it to def filter_results(self): # We want to enter the switch name, not just the port number - # Because we're concatenating a CharField and an Integer, we have to sepcify the output_field + # Because we're concatenating a CharField and an Integer, we have to specify the output_field self.query_set = self.query_set.annotate( full_name=Concat( "switch__name", Value(" "), "port", output_field=CharField() @@ -133,23 +131,29 @@ class PortAutocomplete(AutocompleteViewMixin): class SwitchBayAutocomplete(AutocompleteViewMixin): obj_type = SwitchBay - # Precision on search to add annotations so search behaves more like users expect it to def filter_results(self): - # Comments explain what we try to match + # See RoomAutocomplete.filter_results self.query_set = self.query_set.annotate( full_name=Concat( "building__name", Value(" "), "name" - ), # Match when the user searches "" + ), dorm_name=Concat( "building__dormitory__name", Value(" "), "name" - ), # Match "Dorm Local Sud" + ), dorm_full_name=Concat( "building__dormitory__name", Value(" "), "building__name", Value(" "), "name", - ), # Match "Dorm J Local Sud" + ), + dorm_full_colon_name=Concat( + "building__dormitory__name", + Value(" : "), + "building__name", + Value(" "), + "name", + ), ).all() if self.q: @@ -157,10 +161,9 @@ class SwitchBayAutocomplete(AutocompleteViewMixin): Q(full_name__icontains=self.q) | Q(dorm_name__icontains=self.q) | Q(dorm_full_name__icontains=self.q) + | Q(dorm_full_colon_name__icontains=self.q) ) - return qs - class PortProfileAutocomplete(AutocompleteViewMixin): obj_type = PortProfile From 287df2d1f0748cbe91f258d289203fc34a045d03 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 2 Jan 2021 23:03:13 +0100 Subject: [PATCH 436/490] Return True when list is returned --- machines/models.py | 6 +++--- users/models.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/machines/models.py b/machines/models.py index cb1f835b..df5d3cd2 100644 --- a/machines/models.py +++ b/machines/models.py @@ -398,7 +398,7 @@ class MachineType(RevMixin, AclMixin, models.Model): ) else: return ( - False, + True, _("You don't have the right to use all machine types."), ("machines.use_all_machinetype",), cls.objects.filter( @@ -1001,7 +1001,7 @@ class Extension(RevMixin, AclMixin, models.Model): ) else: return ( - False, + True, _("You don't have the right to list all extensions."), ("machines.use_all_extension",), cls.objects.filter(need_infra=False), @@ -2204,7 +2204,7 @@ class IpList(RevMixin, AclMixin, models.Model): ) else: return ( - False, + True, _("You don't have the right to use all machine types."), ("machines.use_all_machinetype",), cls.objects.filter( diff --git a/users/models.py b/users/models.py index 9fb10972..2b6eaacf 100755 --- a/users/models.py +++ b/users/models.py @@ -2086,7 +2086,7 @@ class Adherent(User): ) else: return ( - False, + True, _("You don't have the right to list all adherents."), ("users.view_user",), cls.objects.none(), From 8beece200de2a1e6dfb3a428eb120e0d79ad0f37 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 2 Jan 2021 23:05:03 +0100 Subject: [PATCH 437/490] Pin specific version --- pip_requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pip_requirements.txt b/pip_requirements.txt index b067c4c5..b0ceffe5 100644 --- a/pip_requirements.txt +++ b/pip_requirements.txt @@ -1,3 +1,3 @@ django-bootstrap3==11.1.0 django-macaddress==1.6.0 -django-autocomplete-light +django-autocomplete-light==3.8.1 From e3445b6a60b3ba23a30d86e16ab038cffac42b2d Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 2 Jan 2021 23:19:49 +0100 Subject: [PATCH 438/490] Move new autocomplete widgets on widget file --- cotisations/forms.py | 11 ++--- logs/forms.py | 4 +- machines/forms.py | 52 +++++++++++------------ multi_op/preferences/forms.py | 4 +- preferences/forms.py | 18 ++++---- re2o/mixins.py | 48 ---------------------- re2o/views.py | 3 -- re2o/widgets.py | 77 +++++++++++++++++++++++++++++++++++ tickets/forms.py | 5 ++- topologie/forms.py | 34 ++++++++-------- users/forms.py | 24 +++++------ 11 files changed, 154 insertions(+), 126 deletions(-) create mode 100644 re2o/widgets.py diff --git a/cotisations/forms.py b/cotisations/forms.py index 4689e3cb..f9e44686 100644 --- a/cotisations/forms.py +++ b/cotisations/forms.py @@ -45,11 +45,8 @@ from django.utils.translation import ugettext_lazy as _ from django.shortcuts import get_object_or_404 from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import ( - FormRevMixin, - AutocompleteModelMixin, - AutocompleteMultipleModelMixin, -) +from re2o.mixins import FormRevMixin +from re2o.widgets import AutocompleteModelWidget from .models import ( Article, Paiement, @@ -84,8 +81,8 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm): model = Facture fields = "__all__" widgets = { - "user": AutocompleteModelMixin(url="/users/user-autocomplete"), - "banque": AutocompleteModelMixin(url="/cotisations/banque-autocomplete"), + "user": AutocompleteModelWidget(url="/users/user-autocomplete"), + "banque": AutocompleteModelWidget(url="/cotisations/banque-autocomplete"), } def clean(self): diff --git a/logs/forms.py b/logs/forms.py index 03045e4a..0a2ca09c 100644 --- a/logs/forms.py +++ b/logs/forms.py @@ -25,7 +25,7 @@ from django import forms from django.forms import Form from django.utils.translation import ugettext_lazy as _ from re2o.base import get_input_formats_help_text -from re2o.mixins import AutocompleteModelMixin +from re2o.widgets import AutocompleteModelWidget import inspect @@ -114,7 +114,7 @@ class ActionsSearchForm(Form): label=_("Performed by"), queryset=users.models.User.objects.all(), required=False, - widget=AutocompleteModelMixin(url="/users/user-autocomplete"), + widget=AutocompleteModelWidget(url="/users/user-autocomplete"), ) action_type = forms.MultipleChoiceField( label=_("Action type"), diff --git a/machines/forms.py b/machines/forms.py index 9a563d9a..ed5975e3 100644 --- a/machines/forms.py +++ b/machines/forms.py @@ -40,10 +40,10 @@ from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import ( - FormRevMixin, - AutocompleteModelMixin, - AutocompleteMultipleModelMixin, +from re2o.mixins import FormRevMixin +from re2o.widgets import ( + AutocompleteModelWidget, + AutocompleteMultipleModelWidget, ) from .models import ( Domain, @@ -75,7 +75,7 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Meta: model = Machine fields = "__all__" - widgets = {"user": AutocompleteModelMixin(url="/users/user-autocomplete")} + widgets = {"user": AutocompleteModelWidget(url="/users/user-autocomplete")} def __init__(self, *args, **kwargs): prefix = kwargs.pop("prefix", self.Meta.model.__name__) @@ -97,11 +97,11 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): model = Interface fields = ["machine", "machine_type", "ipv4", "mac_address", "details"] widgets = { - "machine": AutocompleteModelMixin(url="/machines/machine-autocomplete"), - "machine_type": AutocompleteModelMixin( + "machine": AutocompleteModelWidget(url="/machines/machine-autocomplete"), + "machine_type": AutocompleteModelWidget( url="/machines/machinetype-autocomplete" ), - "ipv4": AutocompleteModelMixin( + "ipv4": AutocompleteModelWidget( url="/machines/iplist-autocomplete", forward=["machine_type"], attrs={ @@ -158,7 +158,7 @@ class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): model = Domain fields = ["name", "extension", "ttl"] widgets = { - "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete") + "extension": AutocompleteModelWidget(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -210,7 +210,7 @@ class MachineTypeForm(FormRevMixin, ModelForm): model = MachineType fields = ["name", "ip_type"] widgets = { - "ip_type": AutocompleteModelMixin(url="/machines/iptype-autocomplete") + "ip_type": AutocompleteModelWidget(url="/machines/iptype-autocomplete") } def __init__(self, *args, **kwargs): @@ -247,9 +247,9 @@ class IpTypeForm(FormRevMixin, ModelForm): model = IpType fields = "__all__" widgets = { - "vlan": AutocompleteModelMixin(url="/machines/vlan-autocomplete"), - "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete"), - "ouverture_ports": AutocompleteModelMixin( + "vlan": AutocompleteModelWidget(url="/machines/vlan-autocomplete"), + "extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"), + "ouverture_ports": AutocompleteModelWidget( url="/machines/ouvertureportlist-autocomplete" ), } @@ -383,8 +383,8 @@ class MxForm(FormRevMixin, ModelForm): model = Mx fields = ["zone", "priority", "name", "ttl"] widgets = { - "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete"), - "name": AutocompleteModelMixin(url="/machines/domain-autocomplete"), + "zone": AutocompleteModelWidget(url="/machines/extension-autocomplete"), + "name": AutocompleteModelWidget(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -422,8 +422,8 @@ class NsForm(FormRevMixin, ModelForm): model = Ns fields = ["zone", "ns", "ttl"] widgets = { - "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete"), - "ns": AutocompleteModelMixin(url="/machines/domain-autocomplete"), + "zone": AutocompleteModelWidget(url="/machines/extension-autocomplete"), + "ns": AutocompleteModelWidget(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -459,7 +459,7 @@ class TxtForm(FormRevMixin, ModelForm): model = Txt fields = "__all__" widgets = { - "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete") + "zone": AutocompleteModelWidget(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -492,7 +492,7 @@ class DNameForm(FormRevMixin, ModelForm): model = DName fields = "__all__" widgets = { - "zone": AutocompleteModelMixin(url="/machines/extension-autocomplete") + "zone": AutocompleteModelWidget(url="/machines/extension-autocomplete") } def __init__(self, *args, **kwargs): @@ -525,8 +525,8 @@ class SrvForm(FormRevMixin, ModelForm): model = Srv fields = "__all__" widgets = { - "extension": AutocompleteModelMixin(url="/machines/extension-autocomplete"), - "target": AutocompleteModelMixin(url="/machines/domain-autocomplete"), + "extension": AutocompleteModelWidget(url="/machines/extension-autocomplete"), + "target": AutocompleteModelWidget(url="/machines/domain-autocomplete"), } def __init__(self, *args, **kwargs): @@ -559,10 +559,10 @@ class NasForm(FormRevMixin, ModelForm): model = Nas fields = "__all__" widgets = { - "nas_type": AutocompleteModelMixin( + "nas_type": AutocompleteModelWidget( url="/machines/machinetype-autocomplete" ), - "machine_type": AutocompleteModelMixin( + "machine_type": AutocompleteModelWidget( url="/machines/machinetype-autocomplete" ), } @@ -597,7 +597,7 @@ class RoleForm(FormRevMixin, ModelForm): model = Role fields = "__all__" widgets = { - "servers": AutocompleteMultipleModelMixin( + "servers": AutocompleteMultipleModelWidget( url="/machines/interface-autocomplete" ) } @@ -635,7 +635,7 @@ class ServiceForm(FormRevMixin, ModelForm): model = Service fields = "__all__" widgets = { - "servers": AutocompleteMultipleModelMixin( + "servers": AutocompleteMultipleModelWidget( url="/machines/interface-autocomplete" ) } @@ -724,7 +724,7 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm): model = Interface fields = ["port_lists"] widgets = { - "port_lists": AutocompleteMultipleModelMixin( + "port_lists": AutocompleteMultipleModelWidget( url="/machines/ouvertureportlist-autocomplete" ) } diff --git a/multi_op/preferences/forms.py b/multi_op/preferences/forms.py index 30aceb36..45941007 100644 --- a/multi_op/preferences/forms.py +++ b/multi_op/preferences/forms.py @@ -29,7 +29,7 @@ each. from django import forms from django.forms import ModelForm, Form from django.utils.translation import ugettext_lazy as _ -from re2o.mixins import AutocompleteMultipleModelMixin +from re2o.widgets import AutocompleteMultipleModelWidget from .models import MultiopOption @@ -41,7 +41,7 @@ class EditMultiopOptionForm(ModelForm): model = MultiopOption fields = "__all__" widgets = { - "enabled_dorm": AutocompleteMultipleModelMixin( + "enabled_dorm": AutocompleteMultipleModelWidget( url="/topologie/dormitory-autocomplete", ), } diff --git a/preferences/forms.py b/preferences/forms.py index a7d03162..e19737c1 100644 --- a/preferences/forms.py +++ b/preferences/forms.py @@ -29,7 +29,11 @@ from django.forms import ModelForm, Form from django.db.models import Q from django import forms from django.utils.translation import ugettext_lazy as _ -from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin +from re2o.mixins import FormRevMixin +from re2o.widgets import ( + AutocompleteModelWidget, + AutocompleteMultipleModelWidget +) from .models import ( OptionalUser, OptionalMachine, @@ -110,14 +114,14 @@ class EditOptionalTopologieForm(ModelForm): automatic_provision_switchs = forms.ModelMultipleChoiceField( Switch.objects.all(), required=False, - widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + widget=AutocompleteMultipleModelWidget(url="/topologie/switch-autocomplete"), ) class Meta: model = OptionalTopologie fields = "__all__" widgets = { - "switchs_ip_type": AutocompleteModelMixin( + "switchs_ip_type": AutocompleteModelWidget( url="/machines/iptype-autocomplete", ), } @@ -176,7 +180,7 @@ class EditAssoOptionForm(ModelForm): model = AssoOption fields = "__all__" widgets = { - "utilisateur_asso": AutocompleteModelMixin( + "utilisateur_asso": AutocompleteModelWidget( url="/users/user-autocomplete", ), } @@ -267,7 +271,7 @@ class MandateForm(ModelForm): model = Mandate fields = "__all__" widgets = { - "president": AutocompleteModelMixin( + "president": AutocompleteModelWidget( url="/users/user-autocomplete", ), } @@ -387,7 +391,7 @@ class RadiusKeyForm(FormRevMixin, ModelForm): members = forms.ModelMultipleChoiceField( queryset=Switch.objects.all(), required=False, - widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + widget=AutocompleteMultipleModelWidget(url="/topologie/switch-autocomplete"), ) class Meta: @@ -413,7 +417,7 @@ class SwitchManagementCredForm(FormRevMixin, ModelForm): members = forms.ModelMultipleChoiceField( Switch.objects.all(), required=False, - widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + widget=AutocompleteMultipleModelWidget(url="/topologie/switch-autocomplete"), ) class Meta: diff --git a/re2o/mixins.py b/re2o/mixins.py index 471f7414..22933e67 100644 --- a/re2o/mixins.py +++ b/re2o/mixins.py @@ -26,7 +26,6 @@ A set of mixins used all over the project to avoid duplicating code from reversion import revisions as reversion from django.db import transaction from django.utils.translation import ugettext as _ -from dal import autocomplete class RevMixin(object): @@ -254,50 +253,3 @@ class AclMixin(object): (permission,), ) - -class AutocompleteModelMixin(autocomplete.ModelSelect2): - """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options - See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 - """ - - def __init__(self, *args, **kwargs): - select2_attrs = kwargs.get("attrs", {}) - kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) - - super().__init__(*args, **kwargs) - - def fill_default_select2_attrs(self, attrs): - """ - See https://select2.org/configuration/options-api - """ - # Display the "x" button to clear the input by default - attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") - # If there are less than 10 results, just show all of them (no need to autocomplete) - attrs["data-minimum-results-for-search"] = attrs.get( - "data-minimum-results-for-search", 10 - ) - return attrs - - -class AutocompleteMultipleModelMixin(autocomplete.ModelSelect2Multiple): - """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options - See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 - """ - - def __init__(self, *args, **kwargs): - select2_attrs = kwargs.get("attrs", {}) - kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) - - super().__init__(*args, **kwargs) - - def fill_default_select2_attrs(self, attrs): - """ - See https://select2.org/configuration/options-api - """ - # Display the "x" button to clear the input by default - attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") - # If there are less than 10 results, just show all of them (no need to autocomplete) - attrs["data-minimum-results-for-search"] = attrs.get( - "data-minimum-results-for-search", 10 - ) - return attrs diff --git a/re2o/views.py b/re2o/views.py index aa8ddc4b..ea22e97c 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -32,8 +32,6 @@ from django.shortcuts import render from django.template.context_processors import csrf from django.conf import settings from django.utils.translation import ugettext as _ -from django.views.decorators.cache import cache_page -from django.contrib.auth.decorators import login_required from django.contrib.auth.mixins import LoginRequiredMixin from django.utils.decorators import method_decorator from dal import autocomplete @@ -47,7 +45,6 @@ from preferences.models import ( Mandate, ) -from .acl import can_list from .contributors import CONTRIBUTORS from importlib import import_module from re2o.settings_local import OPTIONNAL_APPS_RE2O diff --git a/re2o/widgets.py b/re2o/widgets.py new file mode 100644 index 00000000..935806fe --- /dev/null +++ b/re2o/widgets.py @@ -0,0 +1,77 @@ +# -*- mode: python; coding: utf-8 -*- +# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il +# se veut agnostique au réseau considéré, de manière à être installable en +# quelques clics. +# +# Copyright © 2021 Gabriel Détraz +# Copyright © 2021 Jean-Romain Garnier +# +# 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. +""" +Re2o Forms and ModelForms Widgets. + +Used in others forms for using autocomplete engine. +""" + +from django.utils.translation import ugettext as _ +from dal import autocomplete + + +class AutocompleteModelWidget(autocomplete.ModelSelect2): + """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options + See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 + """ + + def __init__(self, *args, **kwargs): + select2_attrs = kwargs.get("attrs", {}) + kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) + + super().__init__(*args, **kwargs) + + def fill_default_select2_attrs(self, attrs): + """ + See https://select2.org/configuration/options-api + """ + # Display the "x" button to clear the input by default + attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") + # If there are less than 10 results, just show all of them (no need to autocomplete) + attrs["data-minimum-results-for-search"] = attrs.get( + "data-minimum-results-for-search", 10 + ) + return attrs + + +class AutocompleteMultipleModelWidget(autocomplete.ModelSelect2Multiple): + """ A mixin subclassing django-autocomplete-light's Select2 model to pass default options + See https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#passing-options-to-select2 + """ + + def __init__(self, *args, **kwargs): + select2_attrs = kwargs.get("attrs", {}) + kwargs["attrs"] = self.fill_default_select2_attrs(select2_attrs) + + super().__init__(*args, **kwargs) + + def fill_default_select2_attrs(self, attrs): + """ + See https://select2.org/configuration/options-api + """ + # Display the "x" button to clear the input by default + attrs["data-allow-clear"] = attrs.get("data-allow-clear", "true") + # If there are less than 10 results, just show all of them (no need to autocomplete) + attrs["data-minimum-results-for-search"] = attrs.get( + "data-minimum-results-for-search", 10 + ) + return attrs diff --git a/tickets/forms.py b/tickets/forms.py index ecde5492..aad78837 100644 --- a/tickets/forms.py +++ b/tickets/forms.py @@ -28,7 +28,8 @@ from django import forms from django.template.loader import render_to_string from django.forms import ModelForm, Form from re2o.field_permissions import FieldPermissionFormMixin -from re2o.mixins import FormRevMixin, AutocompleteModelMixin, AutocompleteMultipleModelMixin +from re2o.mixins import FormRevMixin +from re2o.widgets import AutocompleteModelWidget from django.utils.translation import ugettext_lazy as _ from .models import Ticket, CommentTicket @@ -59,7 +60,7 @@ class EditTicketForm(FormRevMixin, ModelForm): model = Ticket fields = "__all__" widgets = { - "user": AutocompleteModelMixin( + "user": AutocompleteModelWidget( url="/users/user-autocomplete", ), } diff --git a/topologie/forms.py b/topologie/forms.py index e88218fd..300253f6 100644 --- a/topologie/forms.py +++ b/topologie/forms.py @@ -37,10 +37,10 @@ from django.utils.translation import ugettext_lazy as _ from machines.models import Interface from machines.forms import EditMachineForm, NewMachineForm -from re2o.mixins import ( - FormRevMixin, - AutocompleteModelMixin, - AutocompleteMultipleModelMixin, +from re2o.mixins import FormRevMixin +from re2o.widgets import ( + AutocompleteModelWidget, + AutocompleteMultipleModelWidget, ) from .models import ( @@ -67,13 +67,13 @@ class PortForm(FormRevMixin, ModelForm): model = Port fields = "__all__" widgets = { - "switch": AutocompleteModelMixin(url="/topologie/switch-autocomplete"), - "room": AutocompleteModelMixin(url="/topologie/room-autocomplete"), - "machine_interface": AutocompleteModelMixin( + "switch": AutocompleteModelWidget(url="/topologie/switch-autocomplete"), + "room": AutocompleteModelWidget(url="/topologie/room-autocomplete"), + "machine_interface": AutocompleteModelWidget( url="/machine/machine-autocomplete" ), - "related": AutocompleteModelMixin(url="/topologie/port-autocomplete"), - "custom_profile": AutocompleteModelMixin( + "related": AutocompleteModelWidget(url="/topologie/port-autocomplete"), + "custom_profile": AutocompleteModelWidget( url="/topologie/portprofile-autocomplete" ), } @@ -181,10 +181,10 @@ class EditSwitchForm(EditMachineForm): model = Switch fields = "__all__" widgets = { - "switchbay": AutocompleteModelMixin( + "switchbay": AutocompleteModelWidget( url="/topologie/switchbay-autocomplete" ), - "user": AutocompleteModelMixin(url="/users/user-autocomplete"), + "user": AutocompleteModelWidget(url="/users/user-autocomplete"), } @@ -202,7 +202,7 @@ class EditRoomForm(FormRevMixin, ModelForm): model = Room fields = "__all__" widgets = { - "building": AutocompleteModelMixin(url="/topologie/building-autocomplete") + "building": AutocompleteModelWidget(url="/topologie/building-autocomplete") } def __init__(self, *args, **kwargs): @@ -222,7 +222,7 @@ class EditModelSwitchForm(FormRevMixin, ModelForm): members = forms.ModelMultipleChoiceField( Switch.objects.all(), - widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + widget=AutocompleteMultipleModelWidget(url="/topologie/switch-autocomplete"), required=False, ) @@ -261,14 +261,14 @@ class EditSwitchBayForm(FormRevMixin, ModelForm): members = forms.ModelMultipleChoiceField( Switch.objects.all(), required=False, - widget=AutocompleteMultipleModelMixin(url="/topologie/switch-autocomplete"), + widget=AutocompleteMultipleModelWidget(url="/topologie/switch-autocomplete"), ) class Meta: model = SwitchBay fields = "__all__" widgets = { - "building": AutocompleteModelMixin(url="/topologie/building-autocomplete") + "building": AutocompleteModelWidget(url="/topologie/building-autocomplete") } def __init__(self, *args, **kwargs): @@ -315,10 +315,10 @@ class EditPortProfileForm(FormRevMixin, ModelForm): model = PortProfile fields = "__all__" widgets = { - "vlan_tagged": AutocompleteMultipleModelMixin( + "vlan_tagged": AutocompleteMultipleModelWidget( url="/machines/vlan-autocomplete" ), - "vlan_untagged": AutocompleteModelMixin(url="/machines/vlan-autocomplete"), + "vlan_untagged": AutocompleteModelWidget(url="/machines/vlan-autocomplete"), } def __init__(self, *args, **kwargs): diff --git a/users/forms.py b/users/forms.py index dbe1cf7e..172a8c54 100644 --- a/users/forms.py +++ b/users/forms.py @@ -63,10 +63,10 @@ from topologie.models import Port from preferences.models import OptionalUser from re2o.utils import remove_user_room from re2o.base import get_input_formats_help_text -from re2o.mixins import ( - FormRevMixin, - AutocompleteMultipleModelMixin, - AutocompleteModelMixin, +from re2o.mixins import FormRevMixin +from re2o.widgets import ( + AutocompleteMultipleModelWidget, + AutocompleteModelWidget, ) from re2o.field_permissions import FieldPermissionFormMixin @@ -351,14 +351,14 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "room", ] widgets = { - "school": AutocompleteModelMixin(url="/users/school-autocomplete"), - "room": AutocompleteModelMixin( + "school": AutocompleteModelWidget(url="/users/school-autocomplete"), + "room": AutocompleteModelWidget( url="/topologie/room-autocomplete", attrs={ "data-minimum-input-length": 3 # Only trigger autocompletion after 3 characters have been typed }, ), - "shell": AutocompleteModelMixin(url="/users/shell-autocomplete"), + "shell": AutocompleteModelWidget(url="/users/shell-autocomplete"), } force = forms.BooleanField( @@ -623,9 +623,9 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): "mailing", ] widgets = { - "school": AutocompleteModelMixin(url="/users/school-autocomplete"), - "room": AutocompleteModelMixin(url="/topologie/room-autocomplete"), - "shell": AutocompleteModelMixin(url="/users/shell-autocomplete"), + "school": AutocompleteModelWidget(url="/users/school-autocomplete"), + "room": AutocompleteModelWidget(url="/topologie/room-autocomplete"), + "shell": AutocompleteModelWidget(url="/users/shell-autocomplete"), } def clean_telephone(self): @@ -656,10 +656,10 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm): model = Club fields = ["administrators", "members"] widgets = { - "administrators": AutocompleteMultipleModelMixin( + "administrators": AutocompleteMultipleModelWidget( url="/users/adherent-autocomplete" ), - "members": AutocompleteMultipleModelMixin( + "members": AutocompleteMultipleModelWidget( url="/users/adherent-autocomplete" ), } From bdf1a14f71cf5bfdbf2b245dd18b4b29a69444ec Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 2 Jan 2021 23:35:56 +0100 Subject: [PATCH 439/490] Add an unlogged view for registration view --- re2o/views.py | 7 ++++++- topologie/views_autocomplete.py | 4 ++-- users/views_autocomplete.py | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/re2o/views.py b/re2o/views.py index ea22e97c..f5c6e476 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -173,7 +173,7 @@ def handler404(request): return render(request, "errors/404.html", status=404) -class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView): +class AutocompleteUnloggedViewMixin(autocomplete.Select2QuerySetView): obj_type = None # This MUST be overridden by child class query_set = None query_filter = "name__icontains" # Override this if necessary @@ -193,3 +193,8 @@ class AutocompleteViewMixin(LoginRequiredMixin, autocomplete.Select2QuerySetView self.query_set = self.query_set.filter(**{self.query_filter: self.q}) return self.query_set + + +class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteUnloggedViewMixin): + pass + diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index e1069b18..5972da6d 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -36,10 +36,10 @@ from django.db.models.functions import Concat from .models import Room, Dormitory, Building, Switch, PortProfile, Port, SwitchBay -from re2o.views import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin, AutocompleteUnloggedViewMixin -class RoomAutocomplete(AutocompleteViewMixin): +class RoomAutocomplete(AutocompleteUnloggedViewMixin): obj_type = Room # Precision on search to add annotations so search behaves more like users expect it to diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index e202ec4d..219fe2bb 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -33,13 +33,13 @@ from __future__ import unicode_literals from .models import User, School, Adherent, Club, ListShell -from re2o.views import AutocompleteViewMixin +from re2o.views import AutocompleteViewMixin, AutocompleteUnloggedViewMixin from django.db.models import Q, Value, CharField from django.db.models.functions import Concat -class SchoolAutocomplete(AutocompleteViewMixin): +class SchoolAutocomplete(AutocompleteUnloggedViewMixin): obj_type = School From 9669ab6851ebe5771287d0e61dbaa4ea02c716e8 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 2 Jan 2021 23:42:13 +0100 Subject: [PATCH 440/490] Unlogged->LoggedOut --- re2o/views.py | 4 ++-- topologie/views_autocomplete.py | 4 ++-- users/views_autocomplete.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/re2o/views.py b/re2o/views.py index f5c6e476..bf08009a 100644 --- a/re2o/views.py +++ b/re2o/views.py @@ -173,7 +173,7 @@ def handler404(request): return render(request, "errors/404.html", status=404) -class AutocompleteUnloggedViewMixin(autocomplete.Select2QuerySetView): +class AutocompleteLoggedOutViewMixin(autocomplete.Select2QuerySetView): obj_type = None # This MUST be overridden by child class query_set = None query_filter = "name__icontains" # Override this if necessary @@ -195,6 +195,6 @@ class AutocompleteUnloggedViewMixin(autocomplete.Select2QuerySetView): return self.query_set -class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteUnloggedViewMixin): +class AutocompleteViewMixin(LoginRequiredMixin, AutocompleteLoggedOutViewMixin): pass diff --git a/topologie/views_autocomplete.py b/topologie/views_autocomplete.py index 5972da6d..3eb58db4 100644 --- a/topologie/views_autocomplete.py +++ b/topologie/views_autocomplete.py @@ -36,10 +36,10 @@ from django.db.models.functions import Concat from .models import Room, Dormitory, Building, Switch, PortProfile, Port, SwitchBay -from re2o.views import AutocompleteViewMixin, AutocompleteUnloggedViewMixin +from re2o.views import AutocompleteViewMixin, AutocompleteLoggedOutViewMixin -class RoomAutocomplete(AutocompleteUnloggedViewMixin): +class RoomAutocomplete(AutocompleteLoggedOutViewMixin): obj_type = Room # Precision on search to add annotations so search behaves more like users expect it to diff --git a/users/views_autocomplete.py b/users/views_autocomplete.py index 219fe2bb..014dd146 100644 --- a/users/views_autocomplete.py +++ b/users/views_autocomplete.py @@ -33,13 +33,13 @@ from __future__ import unicode_literals from .models import User, School, Adherent, Club, ListShell -from re2o.views import AutocompleteViewMixin, AutocompleteUnloggedViewMixin +from re2o.views import AutocompleteViewMixin, AutocompleteLoggedOutViewMixin from django.db.models import Q, Value, CharField from django.db.models.functions import Concat -class SchoolAutocomplete(AutocompleteUnloggedViewMixin): +class SchoolAutocomplete(AutocompleteLoggedOutViewMixin): obj_type = School From c3a7e054969100ec4be4e839f2f924c3417dda22 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Tue, 5 Jan 2021 21:09:45 +0100 Subject: [PATCH 441/490] Fix accessing view queryset property in api/permissions.py Django would raise a RuntimeError indicating not to evaluate the .queryset attribute directly --- api/permissions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/permissions.py b/api/permissions.py index 646682c4..7ab96f1e 100644 --- a/api/permissions.py +++ b/api/permissions.py @@ -241,7 +241,7 @@ class AutodetectACLPermission(permissions.BasePermission): # Bypass permission verifications if it is a functional view # (permissions are handled by ACL) - if not getattr(view, "queryset", getattr(view, "get_queryset", None)): + if not hasattr(view, "queryset") and not hasattr(view, "get_queryset"): return True if not request.user or not request.user.is_authenticated: From 8101c4782ff3e3445d4440707527452160cc06d3 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Thu, 7 Jan 2021 23:03:50 +0100 Subject: [PATCH 442/490] Make switch port list horizontally scrollable in topology --- static/css/base.css | 1 + 1 file changed, 1 insertion(+) diff --git a/static/css/base.css b/static/css/base.css index 25faf52b..80285178 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -148,6 +148,7 @@ a > i.fa { .table-responsive { overflow: visible; + overflow-x: scroll; } /* Make modal wider on wide screens */ From b9ce083c8861d0afe5a50ae75aad4e5ccad5078f Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Fri, 8 Jan 2021 23:42:15 +0100 Subject: [PATCH 443/490] Fix fucking Js --- users/templates/users/edit_listright.html | 1 + 1 file changed, 1 insertion(+) diff --git a/users/templates/users/edit_listright.html b/users/templates/users/edit_listright.html index 76e3b756..3f23341c 100644 --- a/users/templates/users/edit_listright.html +++ b/users/templates/users/edit_listright.html @@ -54,6 +54,7 @@ with this program; if not, write to the Free Software Foundation, Inc., :data="treeData" :options="treeOptions" @node:checked="onNodeChecked" + @node:unchecked="onNodeUnchecked" />
    From 6558cfc1ebaa36f2d39f29a7dc6ae9026fd9e796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Pi=C3=A9tri?= Date: Fri, 8 Jan 2021 12:35:40 +0100 Subject: [PATCH 444/490] fix: :bug: Fix send_mail_voucher function --- cotisations/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cotisations/utils.py b/cotisations/utils.py index 8166b0be..f484546a 100644 --- a/cotisations/utils.py +++ b/cotisations/utils.py @@ -105,8 +105,8 @@ def send_mail_voucher(invoice, request=None): "lastname": invoice.user.surname, "email": invoice.user.email, "phone": invoice.user.telephone, - "date_end": invoice.get_subscription().latest("date_end").date_end_memb, - "date_begin": invoice.get_subscription().earliest("date_start").date_start_memb, + "date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb, + "date_begin": invoice.get_subscription().earliest("date_start_memb").date_start_memb, } templatename = CotisationsOption.get_cached_value( "voucher_template" From 2846a7fb32184df487bd0d66c39774dcda8ce8d2 Mon Sep 17 00:00:00 2001 From: chirac Date: Fri, 8 Jan 2021 23:46:25 +0100 Subject: [PATCH 445/490] Add erdnaxe proposal of fix because there is no better solution --- machines/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/machines/models.py b/machines/models.py index df5d3cd2..ef7f4182 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1276,10 +1276,17 @@ class SshFp(RevMixin, AclMixin, models.Model): """Get the hashes for the pub key with correct ID. See RFC: 1 is sha1 , 2 is sha256. + + Because of b64 MUST be divided by 4, we add a "padding" = carracter 3 times. + This padding is then ignored if the pubkey is greater than a multiple of 4. + More informations on : https://gist.github.com/perrygeo/ee7c65bb1541ff6ac770 + As said in the thread, this fix is not optimal, however it is very simple as + no options on b64decode function exists. """ + pubkey = base64.b64decode(self.pub_key_entry + "===") return { - "1": hashlib.sha1(base64.b64decode(self.pub_key_entry)).hexdigest(), - "2": hashlib.sha256(base64.b64decode(self.pub_key_entry)).hexdigest(), + "1": hashlib.sha1(pubkey).hexdigest(), + "2": hashlib.sha256(pubkey).hexdigest(), } class Meta: From e8a9e64c3f82fd0612ada4f57268925cf10525c3 Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 9 Jan 2021 00:47:04 +0100 Subject: [PATCH 446/490] Add specific function and check on validation for sshfp --- machines/models.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/machines/models.py b/machines/models.py index ef7f4182..f4991522 100644 --- a/machines/models.py +++ b/machines/models.py @@ -1276,14 +1276,8 @@ class SshFp(RevMixin, AclMixin, models.Model): """Get the hashes for the pub key with correct ID. See RFC: 1 is sha1 , 2 is sha256. - - Because of b64 MUST be divided by 4, we add a "padding" = carracter 3 times. - This padding is then ignored if the pubkey is greater than a multiple of 4. - More informations on : https://gist.github.com/perrygeo/ee7c65bb1541ff6ac770 - As said in the thread, this fix is not optimal, however it is very simple as - no options on b64decode function exists. """ - pubkey = base64.b64decode(self.pub_key_entry + "===") + pubkey = self.base64_pubkey() return { "1": hashlib.sha1(pubkey).hexdigest(), "2": hashlib.sha256(pubkey).hexdigest(), @@ -1303,6 +1297,31 @@ class SshFp(RevMixin, AclMixin, models.Model): def can_delete(self, user_request, *args, **kwargs): return self.machine.can_delete(user_request, *args, **kwargs) + def base64_pubkey(self): + """Function to decode in base64 the pub key entry + + Returns: + Base64 decoded value of pub_key_entry + + Because of b64 MUST be divided by 4, we add a "padding" = carracter 3 times. + This padding is then ignored if the pubkey is greater than a multiple of 4. + More informations on : https://gist.github.com/perrygeo/ee7c65bb1541ff6ac770 + As said in the thread, this fix is not optimal, however it is very simple as + no options on b64decode function exists.""" + return base64.b64decode(self.pub_key_entry + "===") + + def clean(self, *args, **kwargs): + """Check if the pub_key_entry is a valid base64 entry. + + Raises: + ValidationError: the pub key entry is not a valid base64 enty. + """ + try: + self.base64_pubkey() + except ValueError: + raise ValidationError(_("Ssh pub key entry is incorrect base64 entry")) + super(SshFp, self).clean(*args, **kwargs) + def __str__(self): return str(self.algo) + " " + str(self.comment) From 012505fe08459ce95f8f0a6e6e8ddfe2a793c83f Mon Sep 17 00:00:00 2001 From: chirac Date: Sat, 9 Jan 2021 00:51:36 +0100 Subject: [PATCH 447/490] Translation for error message --- machines/locale/fr/LC_MESSAGES/django.po | 609 ++++++++++++----------- 1 file changed, 309 insertions(+), 300 deletions(-) diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index 1709f5e6..b5a5abb0 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-09 00:47+0100\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -34,112 +34,112 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: machines/forms.py:78 +#: machines/forms.py:83 msgid "Machine name" msgstr "Nom de la machine" -#: machines/forms.py:99 machines/templates/machines/aff_machines.html:81 +#: machines/forms.py:117 machines/templates/machines/aff_machines.html:81 msgid "MAC address" msgstr "Adresse MAC" -#: machines/forms.py:100 machines/templates/machines/aff_machinetype.html:32 -#: machines/templates/machines/machine.html:112 +#: machines/forms.py:118 machines/templates/machines/aff_machinetype.html:32 +#: machines/templates/machines/machine.html:120 msgid "Machine type" msgstr "Type de machine" -#: machines/forms.py:101 +#: machines/forms.py:119 msgid "Select a machine type" msgstr "Sélectionnez un type de machine" -#: machines/forms.py:103 +#: machines/forms.py:121 msgid "Automatic IPv4 assignment" msgstr "Assignation automatique IPv4" -#: machines/forms.py:173 +#: machines/forms.py:194 msgid "Current aliases" msgstr "Alias actuels" -#: machines/forms.py:195 +#: machines/forms.py:219 msgid "Machine type to add" msgstr "Type de machine à ajouter" -#: machines/forms.py:196 +#: machines/forms.py:220 msgid "Related IP type" msgstr "Type d'IP relié" -#: machines/forms.py:204 +#: machines/forms.py:228 msgid "Current machine types" msgstr "Types de machines actuels" -#: machines/forms.py:229 +#: machines/forms.py:260 msgid "IP type to add" msgstr "Type d'IP à ajouter" -#: machines/forms.py:258 +#: machines/forms.py:289 msgid "Current IP types" msgstr "Types d'IP actuels" -#: machines/forms.py:281 +#: machines/forms.py:312 msgid "Extension to add" msgstr "Extension à ajouter" -#: machines/forms.py:282 machines/templates/machines/aff_extension.html:37 +#: machines/forms.py:313 machines/templates/machines/aff_extension.html:37 msgid "A record origin" msgstr "Enregistrement A origin" -#: machines/forms.py:283 machines/templates/machines/aff_extension.html:39 +#: machines/forms.py:314 machines/templates/machines/aff_extension.html:39 msgid "AAAA record origin" msgstr "Enregistrement AAAA origin" -#: machines/forms.py:284 +#: machines/forms.py:315 msgid "SOA record to use" msgstr "Enregistrement SOA à utiliser" -#: machines/forms.py:285 +#: machines/forms.py:316 msgid "Sign with DNSSEC" msgstr "Signer avec DNSSEC" -#: machines/forms.py:293 +#: machines/forms.py:324 msgid "Current extensions" msgstr "Extensions actuelles" -#: machines/forms.py:335 +#: machines/forms.py:366 msgid "Current SOA records" msgstr "Enregistrements SOA actuels" -#: machines/forms.py:368 +#: machines/forms.py:403 msgid "Current MX records" msgstr "Enregistrements MX actuels" -#: machines/forms.py:403 +#: machines/forms.py:442 msgid "Current NS records" msgstr "Enregistrements NS actuels" -#: machines/forms.py:433 +#: machines/forms.py:475 msgid "Current TXT records" msgstr "Enregistrements TXT actuels" -#: machines/forms.py:463 +#: machines/forms.py:508 msgid "Current DNAME records" msgstr "Enregistrements DNAME actuels" -#: machines/forms.py:493 +#: machines/forms.py:542 msgid "Current SRV records" msgstr "Enregistrements SRV actuels" -#: machines/forms.py:523 +#: machines/forms.py:580 msgid "Current NAS devices" msgstr "Dispositifs NAS actuels" -#: machines/forms.py:556 +#: machines/forms.py:618 msgid "Current roles" msgstr "Rôles actuels" -#: machines/forms.py:598 +#: machines/forms.py:665 msgid "Current services" msgstr "Services actuels" -#: machines/forms.py:640 +#: machines/forms.py:707 msgid "Current VLANs" msgstr "VLANs actuels" @@ -155,7 +155,7 @@ msgstr "Peut voir un objet machine" msgid "Can change the user of a machine" msgstr "Peut changer l'utilisateur d'une machine" -#: machines/models.py:85 machines/views.py:309 +#: machines/models.py:85 machines/views.py:220 msgid "machine" msgstr "machine" @@ -175,7 +175,7 @@ msgstr "Vous n'avez pas le droit de voir toutes les machines." msgid "Nonexistent user." msgstr "Utilisateur inexistant." -#: machines/models.py:161 machines/models.py:1514 +#: machines/models.py:161 machines/models.py:1594 msgid "You don't have the right to add a machine." msgstr "Vous n'avez pas le droit d'ajouter une machine." @@ -183,7 +183,7 @@ msgstr "Vous n'avez pas le droit d'ajouter une machine." msgid "You don't have the right to add a machine to another user." msgstr "Vous n'avez pas le droit d'ajouter une machine à un autre utilisateur." -#: machines/models.py:174 machines/models.py:1533 +#: machines/models.py:174 machines/models.py:1613 #, python-format msgid "" "You reached the maximum number of interfaces that you are allowed to create " @@ -192,7 +192,7 @@ msgstr "" "Vous avez atteint le nombre maximal d'interfaces que vous pouvez créer vous-" "même (%s)." -#: machines/models.py:199 machines/models.py:1576 +#: machines/models.py:199 machines/models.py:1656 msgid "You don't have the right to edit a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier une machine d'un autre utilisateur." @@ -202,7 +202,7 @@ msgid "You don't have the right to delete a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer une machine d'une autre utilisateur." -#: machines/models.py:245 machines/models.py:2073 +#: machines/models.py:245 machines/models.py:2153 msgid "You don't have the right to view other machines than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." @@ -226,43 +226,43 @@ msgstr "type de machine" msgid "machine types" msgstr "types de machine" -#: machines/models.py:376 +#: machines/models.py:376 machines/models.py:402 machines/models.py:2234 msgid "You don't have the right to use all machine types." msgstr "Vous n'avez pas le droit d'utiliser tous les types de machine." -#: machines/models.py:413 +#: machines/models.py:441 msgid "Network containing the domain's IPv4 range (optional)." msgstr "Réseau contenant la plage IPv4 du domaine (optionnel)." -#: machines/models.py:418 +#: machines/models.py:446 msgid "Netmask for the domain's IPv4 range." msgstr "Masque de sous-réseau pour la plage IPv4 du domaine." -#: machines/models.py:421 +#: machines/models.py:449 msgid "Enable reverse DNS for IPv4." msgstr "Activer DNS inverse pour IPv4." -#: machines/models.py:428 +#: machines/models.py:456 msgid "Enable reverse DNS for IPv6." msgstr "Activer DNS inverse pour IPv6." -#: machines/models.py:435 +#: machines/models.py:463 msgid "Can view an IP type object" msgstr "Peut voir un objet type d'IP" -#: machines/models.py:436 +#: machines/models.py:464 msgid "Can use all IP types" msgstr "Peut utiliser tous les types d'IP" -#: machines/models.py:438 machines/templates/machines/machine.html:108 +#: machines/models.py:466 machines/templates/machines/machine.html:116 msgid "IP type" -msgstr "type d'IP" +msgstr "Type d'IP" -#: machines/models.py:439 +#: machines/models.py:467 msgid "IP types" -msgstr "types d'IP" +msgstr "Types d'IP" -#: machines/models.py:555 +#: machines/models.py:583 msgid "" "One or several IP addresses from the range are affected, impossible to " "delete the range." @@ -270,25 +270,25 @@ msgstr "" "Une ou plusieurs adresses IP de la plage sont affectées, impossible de " "supprimer la plage." -#: machines/models.py:616 +#: machines/models.py:644 msgid "Domaine IPv4 start and stop must be valid" msgstr "Les valeurs IPv4 Domaine ip start et stop doivent être valides" -#: machines/models.py:618 +#: machines/models.py:646 msgid "Range end must be after range start..." msgstr "La fin de la plage doit être après le début..." -#: machines/models.py:623 +#: machines/models.py:651 msgid "The range is too large, you can't create a larger one than a /16." msgstr "" "La plage est trop grande, vous ne pouvez pas en créer une plus grande " "qu'un /16." -#: machines/models.py:631 +#: machines/models.py:659 msgid "The specified range is not disjoint from existing ranges." msgstr "La plage renseignée n'est pas disjointe des plages existantes." -#: machines/models.py:644 +#: machines/models.py:672 msgid "" "If you specify a domain network or netmask, it must contain the domain's IP " "range." @@ -296,47 +296,47 @@ msgstr "" "Si vous renseignez un réseau ou masque de sous-réseau, il doit contenir la " "plage IP du domaine." -#: machines/models.py:699 +#: machines/models.py:727 msgid "v4 multicast management." msgstr "gestion de multidiffusion v4." -#: machines/models.py:700 +#: machines/models.py:728 msgid "v6 multicast management." msgstr "gestion de multidiffusion v6." -#: machines/models.py:703 +#: machines/models.py:731 msgid "Can view a VLAN object" msgstr "Peut voir un objet VLAN" -#: machines/models.py:704 machines/templates/machines/machine.html:160 +#: machines/models.py:732 machines/templates/machines/machine.html:168 msgid "VLAN" msgstr "VLAN" -#: machines/models.py:705 +#: machines/models.py:733 msgid "VLANs" msgstr "VLANs" -#: machines/models.py:724 +#: machines/models.py:752 msgid "MAC-address" msgstr "MAC-address" -#: machines/models.py:739 +#: machines/models.py:767 msgid "Can view a NAS device object" msgstr "Peut voir un objet dispositif NAS" -#: machines/models.py:740 machines/templates/machines/machine.html:164 +#: machines/models.py:768 machines/templates/machines/machine.html:172 msgid "NAS device" -msgstr "dispositif NAS" +msgstr "Dispositif NAS" -#: machines/models.py:741 +#: machines/models.py:769 msgid "NAS devices" -msgstr "dispositifs NAS" +msgstr "Dispositifs NAS" -#: machines/models.py:766 +#: machines/models.py:794 msgid "Contact email address for the zone." msgstr "Adresse mail de contact pour la zone." -#: machines/models.py:770 +#: machines/models.py:798 msgid "" "Seconds before the secondary DNS have to ask the primary DNS serial to " "detect a modification." @@ -344,7 +344,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande au DNS primaire le serial pour " "détecter une modification." -#: machines/models.py:777 +#: machines/models.py:805 msgid "" "Seconds before the secondary DNS ask the serial again in case of a primary " "DNS timeout." @@ -352,7 +352,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande le serial de nouveau dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:784 +#: machines/models.py:812 msgid "" "Seconds before the secondary DNS stop answering requests in case of primary " "DNS timeout." @@ -360,121 +360,127 @@ msgstr "" "Secondes avant que le DNS secondaire arrête de répondre aux requêtes dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:789 machines/models.py:1116 +#: machines/models.py:817 machines/models.py:1170 msgid "Time To Live." msgstr "Temps de vie" -#: machines/models.py:793 +#: machines/models.py:821 msgid "Can view an SOA record object" msgstr "Peut voir un objet enregistrement SOA" -#: machines/models.py:794 machines/templates/machines/aff_extension.html:36 -#: machines/templates/machines/machine.html:120 +#: machines/models.py:822 machines/templates/machines/aff_extension.html:36 +#: machines/templates/machines/machine.html:128 msgid "SOA record" -msgstr "enregistrement SOA" +msgstr "Enregistrement SOA" -#: machines/models.py:795 +#: machines/models.py:823 msgid "SOA records" -msgstr "enregistrements SOA" +msgstr "Enregistrements SOA" -#: machines/models.py:835 +#: machines/models.py:863 msgid "SOA to edit" msgstr "SOA à modifier" -#: machines/models.py:855 +#: machines/models.py:883 msgid "Zone name, must begin with a dot (.example.org)." msgstr "Nom de zone, doit commencer par un point (.example.org)." -#: machines/models.py:863 +#: machines/models.py:891 msgid "A record associated with the zone." msgstr "Enregistrement A associé à la zone." -#: machines/models.py:869 +#: machines/models.py:897 msgid "AAAA record associated with the zone." msgstr "Enregristrement AAAA associé avec la zone." -#: machines/models.py:873 +#: machines/models.py:901 msgid "Should the zone be signed with DNSSEC." msgstr "La zone doit-elle être signée avec DNSSEC." -#: machines/models.py:878 +#: machines/models.py:906 msgid "Can view an extension object" msgstr "Peut voir un objet extension" -#: machines/models.py:879 +#: machines/models.py:907 msgid "Can use all extensions" msgstr "Peut utiliser toutes les extensions" -#: machines/models.py:881 +#: machines/models.py:909 msgid "DNS extension" -msgstr "extension DNS" +msgstr "Extension DNS" -#: machines/models.py:882 +#: machines/models.py:910 msgid "DNS extensions" -msgstr "extensions DNS" +msgstr "Extensions DNS" -#: machines/models.py:952 +#: machines/models.py:980 msgid "You don't have the right to use all extensions." msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." -#: machines/models.py:961 +#: machines/models.py:1005 +#, fuzzy +#| msgid "You don't have the right to use all extensions." +msgid "You don't have the right to list all extensions." +msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." + +#: machines/models.py:1015 msgid "An extension must begin with a dot." msgstr "Une extension doit commencer par un point." -#: machines/models.py:981 machines/models.py:1013 machines/models.py:1045 -#: machines/models.py:1075 machines/models.py:1870 +#: machines/models.py:1035 machines/models.py:1067 machines/models.py:1099 +#: machines/models.py:1129 machines/models.py:1950 msgid "Time To Live (TTL)" msgstr "Temps de vie (TTL)" -#: machines/models.py:985 +#: machines/models.py:1039 msgid "Can view an MX record object" msgstr "Peut voir un objet enregistrement MX" -#: machines/models.py:986 machines/templates/machines/machine.html:124 +#: machines/models.py:1040 machines/templates/machines/machine.html:132 msgid "MX record" -msgstr "enregistrement MX" +msgstr "Enregistrement MX" -#: machines/models.py:987 +#: machines/models.py:1041 msgid "MX records" -msgstr "enregistrements MX" +msgstr "Enregistrements MX" -#: machines/models.py:1017 +#: machines/models.py:1071 msgid "Can view an NS record object" msgstr "Peut voir un objet enregistrement NS" -#: machines/models.py:1018 machines/templates/machines/machine.html:128 +#: machines/models.py:1072 machines/templates/machines/machine.html:136 msgid "NS record" -msgstr "enregistrement NS" +msgstr "Enregistrement NS" -#: machines/models.py:1019 +#: machines/models.py:1073 msgid "NS records" -msgstr "enregistrements NS" +msgstr "Enregistrements NS" -#: machines/models.py:1049 +#: machines/models.py:1103 msgid "Can view a TXT record object" msgstr "Peut voir un objet enregistrement TXT" -#: machines/models.py:1050 machines/templates/machines/machine.html:132 +#: machines/models.py:1104 machines/templates/machines/machine.html:140 msgid "TXT record" msgstr "enregistrement TXT" -#: machines/models.py:1051 +#: machines/models.py:1105 msgid "TXT records" msgstr "enregistrements TXT" -#: machines/models.py:1079 +#: machines/models.py:1133 msgid "Can view a DNAME record object" msgstr "Peut voir un objet enregistrement DNAME" -#: machines/models.py:1080 machines/templates/machines/machine.html:136 +#: machines/models.py:1134 machines/templates/machines/machine.html:144 msgid "DNAME record" msgstr "enregistrement DNAME" -#: machines/models.py:1081 +#: machines/models.py:1135 msgid "DNAME records" msgstr "enregistrements DNAME" -#: machines/models.py:1122 +#: machines/models.py:1176 msgid "" "Priority of the target server (positive integer value, the lower it is, the " "more the server will be used if available)." @@ -482,7 +488,7 @@ msgstr "" "Priorité du serveur cible (entier positif, plus il est bas, plus le serveur " "sera utilisé si disponible)." -#: machines/models.py:1131 +#: machines/models.py:1185 msgid "" "Relative weight for records with the same priority (integer value between 0 " "and 65535)." @@ -490,168 +496,172 @@ msgstr "" "Poids relatif des enregistrements avec la même priorité (entier entre 0 et " "65535)." -#: machines/models.py:1136 +#: machines/models.py:1190 msgid "TCP/UDP port." msgstr "Port TCP/UDP." -#: machines/models.py:1139 +#: machines/models.py:1193 msgid "Target server." msgstr "Serveur cible." -#: machines/models.py:1143 +#: machines/models.py:1197 msgid "Can view an SRV record object" msgstr "Peut voir un objet enregistrement SRV" -#: machines/models.py:1144 machines/templates/machines/machine.html:140 +#: machines/models.py:1198 machines/templates/machines/machine.html:148 msgid "SRV record" msgstr "enregistrement SRV" -#: machines/models.py:1145 +#: machines/models.py:1199 msgid "SRV records" msgstr "enregistrements SRV" -#: machines/models.py:1204 +#: machines/models.py:1258 msgid "SSH public key." msgstr "Clé publique SSH." -#: machines/models.py:1207 +#: machines/models.py:1261 msgid "Comment." msgstr "Commentaire." -#: machines/models.py:1232 +#: machines/models.py:1287 msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" -#: machines/models.py:1233 machines/templates/machines/machine.html:144 -#: machines/views.py:478 +#: machines/models.py:1288 machines/templates/machines/machine.html:152 +#: machines/views.py:387 msgid "SSHFP record" msgstr "enregistrement SSHFP" -#: machines/models.py:1234 +#: machines/models.py:1289 msgid "SSHFP records" msgstr "enregistrements SSHFP" -#: machines/models.py:1272 +#: machines/models.py:1322 +msgid "Ssh pub key entry is incorrect base64 entry" +msgstr "L'entrée Ssh pub key n'est pas une entrée base64 valide" + +#: machines/models.py:1352 msgid "Can view an interface object" msgstr "Peut voir un objet interface" -#: machines/models.py:1273 +#: machines/models.py:1353 msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: machines/models.py:1275 machines/views.py:362 +#: machines/models.py:1355 machines/views.py:271 msgid "interface" msgstr "interface" -#: machines/models.py:1276 +#: machines/models.py:1356 msgid "interfaces" msgstr "interfaces" -#: machines/models.py:1319 +#: machines/models.py:1399 msgid "Unknown vendor." msgstr "Constructeur inconnu." -#: machines/models.py:1389 +#: machines/models.py:1469 msgid "The given MAC address is invalid." msgstr "L'adresse MAC indiquée est invalide." -#: machines/models.py:1398 +#: machines/models.py:1478 msgid "There are no IP addresses available in the slash." msgstr "Il n'y a pas d'adresses IP disponibles dans le slash." -#: machines/models.py:1463 +#: machines/models.py:1543 msgid "The selected IP type is invalid." msgstr "Le type d'IP sélectionné est invalide." -#: machines/models.py:1477 +#: machines/models.py:1557 msgid "MAC address already registered in this machine type/subnet." msgstr "Adresse MAC déjà enregistrée dans ce type de machine/sous-réseau." -#: machines/models.py:1486 +#: machines/models.py:1566 msgid "The IPv4 address and the machine type don't match." msgstr "L'adresse IPv4 et le type de machine ne correspondent pas." -#: machines/models.py:1507 +#: machines/models.py:1587 msgid "Nonexistent machine." msgstr "Machine inexistante." -#: machines/models.py:1524 +#: machines/models.py:1604 msgid "" "You don't have the right to add an interface to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter une interface à une machine d'un autre " "utilisateur." -#: machines/models.py:1554 +#: machines/models.py:1634 msgid "You don't have the right to edit the machine." msgstr "Vous n'avez pas le droit d'éditer une machine." -#: machines/models.py:1600 +#: machines/models.py:1680 msgid "You don't have the right to delete interfaces of another user." msgstr "" "Vous n'avez pas le droit de supprimer une interface d'une autre utilisateur." -#: machines/models.py:1624 +#: machines/models.py:1704 msgid "You don't have the right to view interfaces other than yours." msgstr "Vous n'avez pas le droit de voir d'autres interfaces que les vôtres." -#: machines/models.py:1658 +#: machines/models.py:1738 msgid "If false,the DNS will not provide this ip." msgstr "Si faux, le DNS n'annoncera pas cette ip." -#: machines/models.py:1663 +#: machines/models.py:1743 msgid "Can view an IPv6 addresses list object" msgstr "Peut voir un objet list d'adresses IPv6" -#: machines/models.py:1666 +#: machines/models.py:1746 msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: machines/models.py:1669 machines/views.py:422 +#: machines/models.py:1749 machines/views.py:331 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" -#: machines/models.py:1670 +#: machines/models.py:1750 msgid "IPv6 addresses lists" msgstr "Listes d'adresses IPv6" -#: machines/models.py:1688 machines/models.py:1973 +#: machines/models.py:1768 machines/models.py:2053 msgid "Nonexistent interface." msgstr "Interface inexistante." -#: machines/models.py:1694 +#: machines/models.py:1774 msgid "You don't have the right to add ipv6 to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter des ipv6 à une machine d'un autre " "utilisateur." -#: machines/models.py:1707 +#: machines/models.py:1787 msgid "You don't have the right to change the SLAAC value of an IPv6 address." msgstr "" "Vous n'avez pas le droit de changer la valeur SLAAC d'une adresse IPv6." -#: machines/models.py:1732 +#: machines/models.py:1812 msgid "You don't have the right to edit ipv6 of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier les ipv6 d'une machine d'un autre " "utilisateur." -#: machines/models.py:1757 +#: machines/models.py:1837 msgid "You don't have the right to delete ipv6 of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer les ipv6 d'une machine d'une autre " "utilisateur." -#: machines/models.py:1781 +#: machines/models.py:1861 msgid "You don't have the right to view ipv6 of machines other than yours." msgstr "" "Vous n'avez pas le droit de voir les ipv6 d'autres machines que les vôtres." -#: machines/models.py:1814 +#: machines/models.py:1894 msgid "A SLAAC IP address is already registered." msgstr "Une adresse IP SLAAC est déjà enregistrée." -#: machines/models.py:1828 +#: machines/models.py:1908 msgid "" "The v6 prefix is incorrect and doesn't match the type associated with the " "machine." @@ -659,55 +669,55 @@ msgstr "" "Le préfixe v6 est incorrect et ne correspond pas au type associé à la " "machine." -#: machines/models.py:1863 +#: machines/models.py:1943 msgid "Mandatory and unique, must not contain dots." msgstr "Obligatoire et unique, ne doit pas contenir de points." -#: machines/models.py:1878 +#: machines/models.py:1958 msgid "Can view a domain object" msgstr "Peut voir un objet domaine" -#: machines/models.py:1879 +#: machines/models.py:1959 msgid "Can change the TTL of a domain object" msgstr "Peut changer le TTL d'un objet domaine" -#: machines/models.py:1881 +#: machines/models.py:1961 msgid "domain" msgstr "domaine" -#: machines/models.py:1882 +#: machines/models.py:1962 msgid "domains" msgstr "domaines" -#: machines/models.py:1909 +#: machines/models.py:1989 msgid "You can't create a both A and CNAME record." msgstr "Vous ne pouvez pas créer un enregistrement à la fois A et CNAME." -#: machines/models.py:1912 +#: machines/models.py:1992 msgid "You can't create a CNAME record pointing to itself." msgstr "Vous ne pouvez pas créer un enregistrement CNAME vers lui-même." -#: machines/models.py:1918 +#: machines/models.py:1998 #, python-format msgid "The domain name %s is too long (over 63 characters)." msgstr "Le nom de domaine %s est trop long (plus de 63 caractères)." -#: machines/models.py:1922 +#: machines/models.py:2002 #, python-format msgid "The domain name %s contains forbidden characters." msgstr "Le nom de domaine %s contient des caractères interdits." -#: machines/models.py:1940 +#: machines/models.py:2020 msgid "Invalid extension." msgstr "Extension invalide." -#: machines/models.py:1982 +#: machines/models.py:2062 msgid "You don't have the right to add an alias to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " "utilisateur." -#: machines/models.py:1998 +#: machines/models.py:2078 #, python-format msgid "" "You reached the maximum number of alias that you are allowed to create " @@ -716,164 +726,164 @@ msgstr "" "Vous avez atteint le nombre maximal d'alias que vous pouvez créer vous-même " "(%s)." -#: machines/models.py:2024 +#: machines/models.py:2104 msgid "You don't have the right to edit an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2049 +#: machines/models.py:2129 msgid "" "You don't have the right to delete an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2084 +#: machines/models.py:2164 msgid "You don't have the right to change the domain's TTL." msgstr "Vous n'avez pas le droit de changer le TTL du domaine." -#: machines/models.py:2106 +#: machines/models.py:2186 msgid "Can view an IPv4 addresses list object" msgstr "Peut voir un object liste d'adresses IPv4" -#: machines/models.py:2107 +#: machines/models.py:2187 msgid "IPv4 addresses list" msgstr "Liste d'adresses IPv4" -#: machines/models.py:2108 +#: machines/models.py:2188 msgid "IPv4 addresses lists" msgstr "Listes d'adresses IPv4" -#: machines/models.py:2125 +#: machines/models.py:2205 msgid "The IPv4 address and the range of the IP type don't match." msgstr "L'adresse IPv4 et la plage du type d'IP ne correspondent pas." -#: machines/models.py:2149 +#: machines/models.py:2257 msgid "DHCP server" msgstr "Serveur DHCP" -#: machines/models.py:2150 +#: machines/models.py:2258 msgid "Switches configuration server" msgstr "Serveur de configuration des commutateurs réseau" -#: machines/models.py:2151 +#: machines/models.py:2259 msgid "Recursive DNS server" msgstr "Serveur DNS récursif" -#: machines/models.py:2152 +#: machines/models.py:2260 msgid "NTP server" msgstr "Serveur NTP" -#: machines/models.py:2153 +#: machines/models.py:2261 msgid "RADIUS server" msgstr "Serveur RADIUS" -#: machines/models.py:2154 +#: machines/models.py:2262 msgid "Log server" msgstr "Serveur log" -#: machines/models.py:2155 +#: machines/models.py:2263 msgid "LDAP master server" msgstr "Serveur LDAP maître" -#: machines/models.py:2156 +#: machines/models.py:2264 msgid "LDAP backup server" msgstr "Serveur LDAP de secours" -#: machines/models.py:2157 +#: machines/models.py:2265 msgid "SMTP server" msgstr "Serveur SMTP" -#: machines/models.py:2158 +#: machines/models.py:2266 msgid "postgreSQL server" msgstr "Serveur postgreSQL" -#: machines/models.py:2159 +#: machines/models.py:2267 msgid "mySQL server" msgstr "Serveur mySQL" -#: machines/models.py:2160 +#: machines/models.py:2268 msgid "SQL client" msgstr "Client SQL" -#: machines/models.py:2161 +#: machines/models.py:2269 msgid "Gateway" msgstr "Passerelle" -#: machines/models.py:2169 +#: machines/models.py:2277 msgid "Can view a role object" msgstr "Peut voir un objet rôle" -#: machines/models.py:2170 +#: machines/models.py:2278 msgid "server role" msgstr "rôle de serveur" -#: machines/models.py:2171 +#: machines/models.py:2279 msgid "server roles" msgstr "rôles de serveur" -#: machines/models.py:2210 +#: machines/models.py:2318 msgid "Minimal time before regeneration of the service." msgstr "Temps minimal avant régénération du service." -#: machines/models.py:2214 +#: machines/models.py:2322 msgid "Maximal time before regeneration of the service." msgstr "Temps maximal avant régénération du service." -#: machines/models.py:2219 +#: machines/models.py:2327 msgid "Can view a service object" msgstr "Peut voir un objet service" -#: machines/models.py:2220 +#: machines/models.py:2328 msgid "service to generate (DHCP, DNS, ...)" msgstr "service à générer (DHCP, DNS, ...)" -#: machines/models.py:2221 +#: machines/models.py:2329 msgid "services to generate (DHCP, DNS, ...)" msgstr "services à générer (DHCP, DNS, ...)" -#: machines/models.py:2278 +#: machines/models.py:2386 msgid "Can view a service server link object" msgstr "Peut voir un objet lien service serveur" -#: machines/models.py:2280 +#: machines/models.py:2388 msgid "link between service and server" msgstr "lien entre service et serveur" -#: machines/models.py:2281 +#: machines/models.py:2389 msgid "links between service and server" msgstr "liens entre service et serveur" -#: machines/models.py:2328 +#: machines/models.py:2436 msgid "Name of the ports configuration" msgstr "Nom de la configuration de ports" -#: machines/models.py:2333 +#: machines/models.py:2441 msgid "Can view a ports opening list object" msgstr "Peut voir un objet liste d'ouverture de ports" -#: machines/models.py:2335 +#: machines/models.py:2443 msgid "ports opening list" msgstr "liste d'ouverture de ports" -#: machines/models.py:2336 +#: machines/models.py:2444 msgid "ports opening lists" msgstr "listes d'ouverture de ports" -#: machines/models.py:2351 +#: machines/models.py:2459 msgid "You don't have the right to delete a ports opening list." msgstr "Vous n'avez pas le droit de supprimer une liste d'ouverture de ports." -#: machines/models.py:2355 +#: machines/models.py:2463 msgid "This ports opening list is used." msgstr "Cette liste d'ouverture de ports est utilisée." -#: machines/models.py:2419 +#: machines/models.py:2527 msgid "ports opening" msgstr "ouverture de ports" -#: machines/models.py:2420 +#: machines/models.py:2528 msgid "ports openings" msgstr "ouvertures de ports" @@ -903,7 +913,7 @@ msgstr "Enregistrement" #: machines/templates/machines/aff_extension.html:34 #: machines/templates/machines/aff_srv.html:34 -#: machines/templates/machines/machine.html:116 +#: machines/templates/machines/machine.html:124 msgid "Extension" msgstr "Extension" @@ -983,13 +993,13 @@ msgstr "Afficher les adresses IPv6" msgid "No IPv6" msgstr "Pas d'IPv6" -#: machines/templates/machines/aff_machines.html:116 machines/views.py:291 -#: machines/views.py:406 machines/views.py:462 machines/views.py:518 -#: machines/views.py:587 machines/views.py:650 machines/views.py:716 -#: machines/views.py:768 machines/views.py:820 machines/views.py:872 -#: machines/views.py:927 machines/views.py:979 machines/views.py:1041 -#: machines/views.py:1097 machines/views.py:1149 machines/views.py:1215 -#: machines/views.py:1267 machines/views.py:1621 +#: machines/templates/machines/aff_machines.html:116 machines/views.py:202 +#: machines/views.py:315 machines/views.py:371 machines/views.py:427 +#: machines/views.py:496 machines/views.py:559 machines/views.py:625 +#: machines/views.py:677 machines/views.py:729 machines/views.py:781 +#: machines/views.py:836 machines/views.py:888 machines/views.py:950 +#: machines/views.py:1006 machines/views.py:1058 machines/views.py:1124 +#: machines/views.py:1176 machines/views.py:1530 msgid "Edit" msgstr "Modifier" @@ -1082,7 +1092,7 @@ msgstr "UDP (sortie)" #: machines/templates/machines/index_service.html:30 #: machines/templates/machines/index_sshfp.html:28 #: machines/templates/machines/index_vlan.html:30 -#: machines/templates/machines/machine.html:31 +#: machines/templates/machines/machine.html:30 msgid "Machines" msgstr "Machines" @@ -1164,7 +1174,7 @@ msgid "Expire" msgstr "Expiration" #: machines/templates/machines/aff_srv.html:32 -#: machines/templates/machines/machine.html:152 +#: machines/templates/machines/machine.html:160 msgid "Service" msgstr "Service" @@ -1434,101 +1444,97 @@ msgstr "Ajouter un VLAN" msgid "Delete one or several VLANs" msgstr "Supprimer un ou plusieurs VLANs" -#: machines/templates/machines/machine.html:92 +#: machines/templates/machines/machine.html:104 msgid "Machine" msgstr "Machine" -#: machines/templates/machines/machine.html:96 +#: machines/templates/machines/machine.html:108 msgid "Interface" msgstr "Interface" -#: machines/templates/machines/machine.html:104 +#: machines/templates/machines/machine.html:112 msgid "Domain" msgstr "Domaine" -#: machines/templates/machines/machine.html:148 +#: machines/templates/machines/machine.html:156 msgid "Alias" msgstr "Alias" -#: machines/templates/machines/machine.html:168 +#: machines/templates/machines/machine.html:176 msgid "IPv6 address" msgstr "Adresse IPv6" -#: machines/views.py:144 -msgid "Select a machine type first." -msgstr "Sélectionnez un type de machine d'abord." - -#: machines/views.py:236 +#: machines/views.py:151 msgid "The machine was created." msgstr "La machine a été créée." -#: machines/views.py:245 machines/views.py:341 machines/views.py:382 -#: machines/views.py:440 machines/views.py:495 machines/views.py:567 -#: machines/views.py:633 machines/views.py:699 machines/views.py:751 -#: machines/views.py:803 machines/views.py:855 machines/views.py:910 -#: machines/views.py:962 machines/views.py:1019 machines/views.py:1080 -#: machines/views.py:1132 machines/views.py:1198 machines/views.py:1250 +#: machines/views.py:158 machines/views.py:250 machines/views.py:291 +#: machines/views.py:349 machines/views.py:404 machines/views.py:476 +#: machines/views.py:542 machines/views.py:608 machines/views.py:660 +#: machines/views.py:712 machines/views.py:764 machines/views.py:819 +#: machines/views.py:871 machines/views.py:928 machines/views.py:989 +#: machines/views.py:1041 machines/views.py:1107 machines/views.py:1159 msgid "Add" msgstr "Ajouter" -#: machines/views.py:277 +#: machines/views.py:190 msgid "The machine was edited." msgstr "La machine a été modifiée." -#: machines/views.py:304 +#: machines/views.py:215 msgid "The machine was deleted." msgstr "La machine a été supprimée." -#: machines/views.py:331 +#: machines/views.py:242 msgid "The interface was created." msgstr "L'interface a été créée." -#: machines/views.py:357 +#: machines/views.py:266 msgid "The interface was deleted." msgstr "L'interface a été supprimée." -#: machines/views.py:377 +#: machines/views.py:286 msgid "The IPv6 addresses list was created." msgstr "La liste d'adresses IPv6 a été créée." -#: machines/views.py:398 +#: machines/views.py:307 msgid "The IPv6 addresses list was edited." msgstr "La liste d'adresses IPv6 a été modifiée." -#: machines/views.py:417 +#: machines/views.py:326 msgid "The IPv6 addresses list was deleted." msgstr "La liste d'adresses IPv6 a été supprimée." -#: machines/views.py:435 +#: machines/views.py:344 msgid "The SSHFP record was created." msgstr "L'enregistrement SSHFP a été créé." -#: machines/views.py:454 +#: machines/views.py:363 msgid "The SSHFP record was edited." msgstr "L'enregistrement SSHFP a été modifié." -#: machines/views.py:473 +#: machines/views.py:382 msgid "The SSHFP record was deleted." msgstr "L'enregistrement SSHFP a été supprimé." -#: machines/views.py:492 +#: machines/views.py:401 msgid "The IP type was created." msgstr "Le type d'IP a été créé." -#: machines/views.py:513 +#: machines/views.py:422 msgid "The IP type was edited." msgstr "Le type d'IP a été modifié." -#: machines/views.py:515 +#: machines/views.py:424 msgid "This IP type change would create duplicated domains" msgstr "" "Ce changement de type d'IP causerait des duplications de noms de domaine" -#: machines/views.py:537 +#: machines/views.py:446 msgid "The IP type was deleted." msgstr "Le type d'IP a été supprimé." -#: machines/views.py:543 +#: machines/views.py:452 #, python-format msgid "" "The IP type %s is assigned to at least one machine, you can't delete it." @@ -1536,33 +1542,33 @@ msgstr "" "Le type d'IP %s est assigné à au moins une machine, vous ne pouvez pas le " "supprimer." -#: machines/views.py:551 machines/views.py:617 machines/views.py:683 -#: machines/views.py:737 machines/views.py:789 machines/views.py:841 -#: machines/views.py:894 machines/views.py:948 machines/views.py:1000 -#: machines/views.py:1064 machines/views.py:1118 machines/views.py:1173 -#: machines/views.py:1236 machines/views.py:1288 +#: machines/views.py:460 machines/views.py:526 machines/views.py:592 +#: machines/views.py:646 machines/views.py:698 machines/views.py:750 +#: machines/views.py:803 machines/views.py:857 machines/views.py:909 +#: machines/views.py:973 machines/views.py:1027 machines/views.py:1082 +#: machines/views.py:1145 machines/views.py:1197 msgid "Delete" msgstr "Supprimer" -#: machines/views.py:564 +#: machines/views.py:473 msgid "The machine type was created." msgstr "Le type de machine a été créé." -#: machines/views.py:582 +#: machines/views.py:491 msgid "The machine type was edited." msgstr "Le type de machine a été modifié." -#: machines/views.py:584 +#: machines/views.py:493 msgid "This machine type change would create duplicated domains" msgstr "" "Ce changement de type de machine causerait des duplications de noms de " "domaines" -#: machines/views.py:603 +#: machines/views.py:512 msgid "The machine type was deleted." msgstr "Le type de machine a été supprimé." -#: machines/views.py:609 +#: machines/views.py:518 #, python-format msgid "" "The machine type %s is assigned to at least one machine, you can't delete it." @@ -1570,226 +1576,226 @@ msgstr "" "Le type de machine %s est assigné à au moins un machine, vous ne pouvez pas " "le supprimer." -#: machines/views.py:630 +#: machines/views.py:539 msgid "The extension was created." msgstr "L'extension a été créée." -#: machines/views.py:647 +#: machines/views.py:556 msgid "The extension was edited." msgstr "L'extension a été modifiée." -#: machines/views.py:666 +#: machines/views.py:575 msgid "The extension was deleted." msgstr "L'extension a été supprimée." -#: machines/views.py:672 +#: machines/views.py:581 #, python-format msgid "The extension %s is assigned to following %s : %s, you can't delete it." msgstr "" "L'extension %s est assignée aux %s suivants : %s , vous ne pouvez pas le " "supprimer." -#: machines/views.py:696 +#: machines/views.py:605 msgid "The SOA record was created." msgstr "L'enregistrement SOA a été créé." -#: machines/views.py:713 +#: machines/views.py:622 msgid "The SOA record was edited." msgstr "L'enregistrement SOA a été modifié." -#: machines/views.py:730 +#: machines/views.py:639 msgid "The SOA record was deleted." msgstr "L'enregistrement SOA a été supprimé." -#: machines/views.py:733 +#: machines/views.py:642 #, python-format msgid "Error: the SOA record %s can't be deleted." msgstr "Erreur : l'enregistrement SOA %s ne peut pas être supprimé." -#: machines/views.py:748 +#: machines/views.py:657 msgid "The MX record was created." msgstr "L'enregistrement MX a été créé." -#: machines/views.py:765 +#: machines/views.py:674 msgid "The MX record was edited." msgstr "L'enregistrement MX a été modifié." -#: machines/views.py:782 +#: machines/views.py:691 msgid "The MX record was deleted." msgstr "L'enregistrement MX a été supprimé." -#: machines/views.py:785 +#: machines/views.py:694 #, python-format msgid "Error: the MX record %s can't be deleted." msgstr "Erreur : l'enregistrement MX %s ne peut pas être supprimé." -#: machines/views.py:800 +#: machines/views.py:709 msgid "The NS record was created." msgstr "L'enregistrement NS a été créé." -#: machines/views.py:817 +#: machines/views.py:726 msgid "The NS record was edited." msgstr "L'enregistrement NS a été modifié." -#: machines/views.py:834 +#: machines/views.py:743 msgid "The NS record was deleted." msgstr "L'enregistrement NS a été supprimé." -#: machines/views.py:837 +#: machines/views.py:746 #, python-format msgid "Error: the NS record %s can't be deleted." msgstr "Erreur : l'enregistrement NS %s ne peut pas être supprimé." -#: machines/views.py:852 +#: machines/views.py:761 msgid "The DNAME record was created." msgstr "L'enregistrement DNAME a été créé." -#: machines/views.py:869 +#: machines/views.py:778 msgid "The DNAME record was edited." msgstr "L'enregistrement DNAME a été modifié." -#: machines/views.py:886 +#: machines/views.py:795 msgid "The DNAME record was deleted." msgstr "L'enregistrement DNAME a été supprimé." -#: machines/views.py:890 +#: machines/views.py:799 #, python-format msgid "Error: the DNAME record %s can't be deleted." msgstr "Erreur : l'enregistrement DNAME %s ne peut pas être supprimé." -#: machines/views.py:907 +#: machines/views.py:816 msgid "The TXT record was created." msgstr "L'enregistrement TXT a été créé." -#: machines/views.py:924 +#: machines/views.py:833 msgid "The TXT record was edited." msgstr "L'enregistrement TXT a été modifié." -#: machines/views.py:941 +#: machines/views.py:850 msgid "The TXT record was deleted." msgstr "L'enregistrement TXT a été supprimé." -#: machines/views.py:944 +#: machines/views.py:853 #, python-format msgid "Error: the TXT record %s can't be deleted." msgstr "Erreur : l'enregistrement %s ne peut pas être supprimé." -#: machines/views.py:959 +#: machines/views.py:868 msgid "The SRV record was created." msgstr "L'enregistrement SRV a été créé." -#: machines/views.py:976 +#: machines/views.py:885 msgid "The SRV record was edited." msgstr "L'enregistrement SRV a été modifié." -#: machines/views.py:993 +#: machines/views.py:902 msgid "The SRV record was deleted." msgstr "L'enregistrement SRV a été supprimé." -#: machines/views.py:996 +#: machines/views.py:905 #, python-format msgid "Error: the SRV record %s can't be deleted." msgstr "Erreur : l'enregistrement SRV %s ne peut pas être supprimé." -#: machines/views.py:1014 +#: machines/views.py:923 msgid "The alias was created." msgstr "L'alias a été créé." -#: machines/views.py:1033 +#: machines/views.py:942 msgid "The alias was edited." msgstr "L'alias a été modifié." -#: machines/views.py:1055 +#: machines/views.py:964 #, python-format msgid "The alias %s was deleted." msgstr "L'alias %s a été supprimé." -#: machines/views.py:1058 +#: machines/views.py:967 #, python-format msgid "Error: the alias %s can't be deleted." msgstr "Erreur : l'alias %s ne peut pas être supprimé." -#: machines/views.py:1077 +#: machines/views.py:986 msgid "The role was created." msgstr "Le rôle a été créé." -#: machines/views.py:1094 +#: machines/views.py:1003 msgid "The role was edited." msgstr "Le rôle a été modifié." -#: machines/views.py:1111 +#: machines/views.py:1020 msgid "The role was deleted." msgstr "Le rôle a été supprimé." -#: machines/views.py:1114 +#: machines/views.py:1023 #, python-format msgid "Error: the role %s can't be deleted." msgstr "Erreur : le rôle %s ne peut pas être supprimé." -#: machines/views.py:1129 +#: machines/views.py:1038 msgid "The service was created." msgstr "Le service a été créé." -#: machines/views.py:1146 +#: machines/views.py:1055 msgid "The service was edited." msgstr "Le service a été modifié." -#: machines/views.py:1165 +#: machines/views.py:1074 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: machines/views.py:1169 +#: machines/views.py:1078 #, python-format msgid "Error: the service %s can't be deleted." msgstr "Erreur : le service %s ne peut pas être supprimé." -#: machines/views.py:1195 +#: machines/views.py:1104 msgid "The VLAN was created." msgstr "Le VLAN a été créé." -#: machines/views.py:1212 +#: machines/views.py:1121 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: machines/views.py:1229 +#: machines/views.py:1138 msgid "The VLAN was deleted." msgstr "Le VLAN a été supprimé." -#: machines/views.py:1232 +#: machines/views.py:1141 #, python-format msgid "Error: the VLAN %s can't be deleted." msgstr "Erreur : le VLAN %s ne peut pas être supprimé." -#: machines/views.py:1247 +#: machines/views.py:1156 msgid "The NAS device was created." msgstr "Le dispositif NAS a été créé." -#: machines/views.py:1264 +#: machines/views.py:1173 msgid "The NAS device was edited." msgstr "Le dispositif NAS a été modifié." -#: machines/views.py:1281 +#: machines/views.py:1190 msgid "The NAS device was deleted." msgstr "Le dispositif NAS a été supprimé." -#: machines/views.py:1284 +#: machines/views.py:1193 #, python-format msgid "Error: the NAS device %s can't be deleted." msgstr "Erreur : le dispositif NAS %s ne peut pas être supprimé." -#: machines/views.py:1547 +#: machines/views.py:1456 msgid "The ports list was edited." msgstr "La liste de ports a été modifiée." -#: machines/views.py:1561 +#: machines/views.py:1470 msgid "The ports list was deleted." msgstr "La liste de ports a été supprimée." -#: machines/views.py:1586 +#: machines/views.py:1495 msgid "The ports list was created." msgstr "La liste de ports a été créée." -#: machines/views.py:1607 +#: machines/views.py:1516 msgid "" "Warning: the IP address is not public, the opening won't have any effect in " "v4." @@ -1797,6 +1803,9 @@ msgstr "" "Attention : l'adresse IP n'est pas publique, l'ouverture n'aura pas d'effet " "en v4." -#: machines/views.py:1618 +#: machines/views.py:1527 msgid "The ports configuration was edited." msgstr "La configuration de ports a été modifiée." + +#~ msgid "Select a machine type first." +#~ msgstr "Sélectionnez un type de machine d'abord." From 750bb7dd1bfb528bd424d51a2ce4b9dffa32919c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Pi=C3=A9tri?= Date: Sat, 9 Jan 2021 19:53:37 +0100 Subject: [PATCH 448/490] fix: :children_crossing: Move user menu back to the right side of the screen (Fix #306) --- templates/base.html | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/templates/base.html b/templates/base.html index 36fc45e4..324891d0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -82,18 +82,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    -
    - {% include 'sidebar.html' %} - - {% block sidebar %} - {% endblock %} +
    -
    +
    {# Display django.contrib.messages as Bootstrap alerts #} {% bootstrap_messages %} {% block content %}{% endblock %}
    -
    + +
    + {% include 'sidebar.html' %} + + {% block sidebar %} + {% endblock %}
    From f2afe8e7bdc1c75df600f5a423afb5203819efb9 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 27 Dec 2020 21:02:31 +0100 Subject: [PATCH 449/490] Preferences model creation --- preferences/migrations/0001_squashed_0071.py | 820 +++++++++++++++++++ 1 file changed, 820 insertions(+) create mode 100644 preferences/migrations/0001_squashed_0071.py diff --git a/preferences/migrations/0001_squashed_0071.py b/preferences/migrations/0001_squashed_0071.py new file mode 100644 index 00000000..2d8c620c --- /dev/null +++ b/preferences/migrations/0001_squashed_0071.py @@ -0,0 +1,820 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import re2o.mixins +import re2o.aes_field + + +class Migration(migrations.Migration): + initial = True + dependencies = [] + replaces = [ + ("preferences", "0001_initial.py"), + ("preferences", "0001_squashed_0071.py"), + ("preferences", "0002_auto_20170625_1923.py"), + ("preferences", "0003_optionaluser_solde_negatif.py"), + ("preferences", "0004_assooption_services.py"), + ("preferences", "0005_auto_20170824_0139.py"), + ("preferences", "0006_auto_20170824_0143.py"), + ("preferences", "0007_auto_20170824_2056.py"), + ("preferences", "0008_auto_20170824_2122.py"), + ("preferences", "0009_assooption_utilisateur_asso.py"), + ("preferences", "0010_auto_20170825_0459.py"), + ("preferences", "0011_auto_20170825_2307.py"), + ("preferences", "0012_generaloption_req_expire_hrs.py"), + ("preferences", "0013_generaloption_site_name.py"), + ("preferences", "0014_generaloption_email_from.py"), + ("preferences", "0015_optionaltopologie_radius_general_policy.py"), + ("preferences", "0016_auto_20170902_1520.py"), + ("preferences", "0017_mailmessageoption.py"), + ("preferences", "0018_optionaltopologie_mac_autocapture.py"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture.py"), + ("preferences", "0020_optionalmachine_ipv6.py"), + ("preferences", "0021_auto_20171015_1741.py"), + ("preferences", "0022_auto_20171015_1758.py"), + ("preferences", "0023_auto_20171015_2033.py"), + ("preferences", "0024_optionaluser_all_can_create.py"), + ("preferences", "0025_auto_20171231_2142.py"), + ("preferences", "0025_generaloption_general_message.py"), + ("preferences", "0026_auto_20171216_0401.py"), + ("preferences", "0027_merge_20180106_2019.py"), + ("preferences", "0028_assooption_description.py"), + ("preferences", "0028_auto_20180111_1129.py"), + ("preferences", "0028_auto_20180128_2203.py"), + ("preferences", "0029_auto_20180111_1134.py"), + ("preferences", "0029_auto_20180318_0213.py"), + ("preferences", "0029_auto_20180318_1005.py"), + ("preferences", "0030_auto_20180111_2346.py"), + ("preferences", "0030_merge_20180320_1419.py"), + ("preferences", "0031_auto_20180323_0218.py"), + ("preferences", "0031_optionaluser_self_adhesion.py"), + ("preferences", "0032_optionaluser_min_online_payment.py"), + ("preferences", "0032_optionaluser_shell_default.py"), + ("preferences", "0033_accueiloption.py"), + ("preferences", "0033_generaloption_gtu_sum_up.py"), + ("preferences", "0034_auto_20180114_2025.py"), + ("preferences", "0034_auto_20180416_1120.py"), + ("preferences", "0035_auto_20180114_2132.py"), + ("preferences", "0035_optionaluser_allow_self_subscription.py"), + ("preferences", "0036_auto_20180114_2141.py"), + ("preferences", "0037_auto_20180114_2156.py"), + ("preferences", "0038_auto_20180114_2209.py"), + ("preferences", "0039_auto_20180115_0003.py"), + ("preferences", "0040_auto_20180129_1745.py"), + ("preferences", "0041_merge_20180130_0052.py"), + ("preferences", "0042_auto_20180222_1743.py"), + ("preferences", "0043_optionalmachine_create_machine.py"), + ("preferences", "0044_remove_payment_pass.py"), + ("preferences", "0045_remove_unused_payment_fields.py"), + ("preferences", "0046_optionaluser_mail_extension.py"), + ("preferences", "0047_mailcontact.py"), + ("preferences", "0048_auto_20180811_1515.py"), + ("preferences", "0049_optionaluser_self_change_shell.py"), + ("preferences", "0050_auto_20180818_1329.py"), + ("preferences", "0051_auto_20180919_2225.py"), + ("preferences", "0052_optionaluser_delete_notyetactive.py"), + ("preferences", "0053_optionaluser_self_change_room.py"), + ("preferences", "0055_generaloption_main_site_url.py"), + ("preferences", "0056_1_radiusoption.py"), + ("preferences", "0056_2_radiusoption.py"), + ("preferences", "0056_3_radiusoption.py"), + ("preferences", "0056_4_radiusoption.py"), + ("preferences", "0057_optionaluser_all_users_active.py"), + ("preferences", "0058_auto_20190108_1650.py"), + ("preferences", "0059_auto_20190120_1739.py"), + ("preferences", "0060_auto_20190712_1821.py"), + ("preferences", "0061_optionaluser_allow_archived_connexion.py"), + ("preferences", "0062_auto_20190910_1909.py"), + ("preferences", "0063_mandate.py"), + ("preferences", "0064_auto_20191008_1335.py"), + ("preferences", "0065_auto_20191010_1227.py"), + ("preferences", "0066_optionalmachine_default_dns_ttl.py"), + ("preferences", "0067_auto_20191120_0159.py"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation.py"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed.py"), + ("preferences", "0070_auto_20200419_0225.py"), + ("preferences", "0071_optionaluser_self_change_pseudo.py"), + ] + operations = [ + migrations.CreateModel( + name="OptionalUser", + bases=(re2o.mixins.AclMixin, models.Model), + options={ + "permissions": (("view_optionaluser", "Can view the user options"),), + "verbose_name": "user options", + }, + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("is_tel_mandatory", models.BooleanField(default=True)), + ("gpg_fingerprint", models.BooleanField(default=True)), + ("all_can_create_club", models.BooleanField(default=False)), + ("all_can_create_adherent", models.BooleanField(default=False)), + ("self_change_shell", models.BooleanField(default=False)), + ("self_change_pseudo", models.BooleanField(default=True)), + ( + "self_room_policy", + models.CharField( + choices=[ + ("DISABLED", "Users can't select their room"), + ( + "ONLY_INACTIVE", + "Users can only select a room occupied by a user with a disabled connection.", + ), + ("ALL_ROOM", "Users can select all rooms"), + ], + default="DISABLED", + help_text="Policy on self users room edition", + max_length=32, + ), + ), + ("local_email_accounts_enabled", models.BooleanField(default=False)), + ( + "local_email_domain", + models.CharField( + default="@example.org", + help_text="Domain to use for local email accounts.", + max_length=32, + ), + ), + ( + "max_email_address", + models.IntegerField( + default=15, + help_text="Maximum number of local email addresses for a standard user.", + ), + ), + ( + "delete_notyetactive", + models.IntegerField( + default=15, + help_text="Not yet active users will be deleted after this number of days.", + ), + ), + ( + "disable_emailnotyetconfirmed", + models.IntegerField( + default=2, + help_text="Users with an email address not yet confirmed will be disabled after this number of days.", + ), + ), + ("self_adhesion", models.BooleanField(default=False)), + ("all_users_active", models.BooleanField(default=False)), + ( + "allow_set_password_during_user_creation", + models.BooleanField(default=False), + ), + ("allow_archived_connexion", models.BooleanField(default=False)), + ], + ), + migrations.CreateModel( + name="OptionalMachine", + bases=(re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("password_machine", models.BooleanField(default=False)), + ("max_lambdauser_interfaces", models.IntegerField(default=10)), + ( + "ipv6_mode", + models.CharField( + choices=[ + ("SLAAC", "Automatic configuration by RA"), + ("DHCPV6", "IP addresses assignment by DHCPv6"), + ("DISABLED", "Disabled"), + ], + default="DISABLED", + max_length=32, + ), + ), + ("create_machine", models.BooleanField(default=True)), + ( + "default_dns_ttl", + models.PositiveIntegerField( + default=172800, + verbose_name="default Time To Live (TTL) for CNAME, A and AAAA records", + ), + ), + ], + options={ + "permissions": ( + ("view_optionalmachine", "Can view the machine options"), + ), + "verbose_name": "machine options", + }, + ), + migrations.CreateModel( + name="OptionalTopologie", + bases=(re2o.mixins.AclMixin, models.Model), + options={ + "permissions": ( + ("view_optionaltopologie", "Can view the topology options"), + ), + "verbose_name": "topology options", + }, + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("switchs_web_management", models.BooleanField(default=False)), + ("switchs_web_management_ssl", models.BooleanField(default=False)), + ("switchs_rest_management", models.BooleanField(default=False)), + ( + "switchs_provision", + models.CharField( + choices=[("sftp", "SFTP"), ("tftp", "TFTP")], + default="tftp", + help_text="Provision of configuration mode for switches.", + max_length=32, + ), + ), + ( + "sftp_login", + models.CharField( + blank=True, + help_text="SFTP login for switches.", + max_length=32, + null=True, + ), + ), + ( + "sftp_pass", + re2o.aes_field.AESEncryptedField( + blank=True, help_text="SFTP password.", max_length=63, null=True + ), + ), + ], + ), + migrations.CreateModel( + name="RadiusKey", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "radius_key", + re2o.aes_field.AESEncryptedField( + help_text="Clef radius", max_length=255 + ), + ), + ( + "comment", + models.CharField( + blank=True, + help_text="Commentaire de cette clef", + max_length=255, + null=True, + ), + ), + ( + "default_switch", + models.BooleanField( + default=True, + help_text="Clef par défaut des switchs", + unique=True, + ), + ), + ], + options={ + "permissions": (("view_radiuskey", "Can view a RADIUS key object"),), + "verbose_name": "RADIUS key", + "verbose_name_plural": "RADIUS keys", + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name="SwitchManagementCred", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "management_id", + models.CharField(help_text="Login du switch", max_length=63), + ), + ( + "management_pass", + re2o.aes_field.AESEncryptedField( + help_text="Mot de passe", max_length=63 + ), + ), + ( + "default_switch", + models.BooleanField( + default=True, + help_text="Creds par défaut des switchs", + unique=True, + ), + ), + ], + options={ + "permissions": ( + ( + "view_switchmanagementcred", + "Can view a switch management credentials object", + ), + ), + "verbose_name": "switch management credentials", + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name="Reminder", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "days", + models.IntegerField( + default=7, + help_text="Délais entre le mail et la fin d'adhésion", + unique=True, + ), + ), + ( + "message", + models.CharField( + blank=True, + default="", + help_text="Message affiché spécifiquement pour ce rappel", + max_length=255, + null=True, + ), + ), + ], + options={ + "permissions": (("view_reminder", "Can view a reminder object"),), + "verbose_name": "reminder", + "verbose_name_plural": "reminders", + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name="GeneralOption", + bases=(re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "general_message_fr", + models.TextField( + blank=True, + default="", + help_text="General message displayed on the French version of the website (e.g. in case of maintenance).", + ), + ), + ( + "general_message_en", + models.TextField( + blank=True, + default="", + help_text="General message displayed on the English version of the website (e.g. in case of maintenance).", + ), + ), + ("search_display_page", models.IntegerField(default=15)), + ("pagination_number", models.IntegerField(default=25)), + ("pagination_large_number", models.IntegerField(default=8)), + ("req_expire_hrs", models.IntegerField(default=48)), + ("site_name", models.CharField(default="Re2o", max_length=32)), + ( + "email_from", + models.EmailField(default="www-data@example.com", max_length=254), + ), + ( + "main_site_url", + models.URLField(default="http://re2o.example.org", max_length=255), + ), + ("GTU_sum_up", models.TextField(blank=True, default="")), + ( + "GTU", + models.FileField(blank=True, default="", null=True, upload_to=""), + ), + ], + options={ + "permissions": ( + ("view_generaloption", "Can view the general options"), + ), + "verbose_name": "general options", + }, + ), + migrations.CreateModel( + name="Service", + options={ + "permissions": (("view_service", "Can view the service options"),), + "verbose_name": "service", + "verbose_name_plural": "services", + }, + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=32)), + ("url", models.URLField()), + ("description", models.TextField()), + ("image", models.ImageField(upload_to="logo")), + ], + ), + 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="Contact email adress", + max_length=254, + ), + ), + ( + "commentary", + models.CharField( + blank=True, + help_text="Description of the associated email adress.", + max_length=256, + null=True, + ), + ), + ], + options={ + "permissions": ( + ("view_mailcontact", "Can view a contact email address object"), + ), + "verbose_name": "contact email address", + "verbose_name_plural": "contact email addresses", + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name="Mandate", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("start_date", models.DateTimeField(verbose_name="start date")), + ( + "end_date", + models.DateTimeField( + blank=True, null=True, verbose_name="end date" + ), + ), + ], + options={ + "verbose_name": "Mandate", + "verbose_name_plural": "Mandates", + "permissions": (("view_mandate", "Can view a mandate"),), + }, + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name="AssoOption", + bases=(re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + default="Networking organisation school Something", + max_length=256, + ), + ), + ("siret", models.CharField(default="00000000000000", max_length=32)), + ( + "adresse1", + models.CharField(default="Threadneedle Street", max_length=128), + ), + ( + "adresse2", + models.CharField(default="London EC2R 8AH", max_length=128), + ), + ("contact", models.EmailField(default="contact@example.org")), + ("telephone", models.CharField(max_length=15, default="0000000000")), + ("pseudo", models.CharField(default="Organisation", max_length=32)), + ("description", models.TextField(null=True, blank=True)), + ], + options={ + "permissions": ( + ("view_assooption", "Can view the organisation preferences"), + ), + "verbose_name": "organisation preferences", + }, + ), + migrations.CreateModel( + name="HomeOption", + bases=(re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("facebook_url", models.URLField(null=True, blank=True)), + ("twitter_url", models.URLField(null=True, blank=True)), + ( + "twitter_account_name", + models.CharField(max_length=32, null=True, blank=True), + ), + ], + options={ + "permissions": ( + ("view_homeoption", "Can view the homepage preferences"), + ), + "verbose_name": "homepage preferences", + }, + ), + migrations.CreateModel( + name="MailMessageOption", + bases=(re2o.mixins.AclMixin, models.Model), + options={ + "permissions": ( + ( + "view_mailmessageoption", + "Can view the email message preferences", + ), + ), + "verbose_name": "email message preferences", + }, + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "welcome_mail_fr", + models.TextField( + default="", blank=True, help_text="Welcome email in French." + ), + ), + ( + "welcome_mail_en", + models.TextField( + default="", blank=True, help_text="Welcome email in English." + ), + ), + ], + ), + migrations.CreateModel( + name="RadiusAttribute", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "attribute", + models.CharField( + max_length=255, + verbose_name="attribute", + help_text="See https://freeradius.org/rfc/attributes.html.", + ), + ), + ("value", models.CharField(max_length=255, verbose_name="value")), + ( + "comment", + models.TextField( + verbose_name="comment", + help_text="Use this field to document this attribute.", + blank=True, + default="", + ), + ), + ], + options={ + "verbose_name": "RADIUS attribute", + "verbose_name_plural": "RADIUS attributes", + }, + ), + migrations.CreateModel( + name="RadiusOption", + bases=(re2o.mixins.AclMixin, models.Model), + options={ + "verbose_name": "RADIUS policy", + "verbose_name_plural": "RADIUS policies", + }, + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "radius_general_policy", + models.CharField( + choices=[ + ("MACHINE", "On the IP range's VLAN of the machine"), + ( + "DEFINED", + 'Preset in "VLAN for machines accepted by RADIUS"', + ), + ], + default="DEFINED", + max_length=32, + ), + ), + ( + "unknown_machine", + models.CharField( + choices=[ + ("REJECT", "Reject the machine"), + ("SET_VLAN", "Place the machine on the VLAN"), + ], + default="REJECT", + max_length=32, + verbose_name="policy for unknown machines", + ), + ), + ( + "unknown_port", + models.CharField( + choices=[ + ("REJECT", "Reject the machine"), + ("SET_VLAN", "Place the machine on the VLAN"), + ], + default="REJECT", + max_length=32, + verbose_name="policy for unknown ports", + ), + ), + ( + "unknown_room", + models.CharField( + choices=[ + ("REJECT", "Reject the machine"), + ("SET_VLAN", "Place the machine on the VLAN"), + ], + default="REJECT", + max_length=32, + verbose_name="Policy for machines connecting from unregistered rooms (relevant on ports with STRICT RADIUS mode)", + ), + ), + ( + "non_member", + models.CharField( + choices=[ + ("REJECT", "Reject the machine"), + ("SET_VLAN", "Place the machine on the VLAN"), + ], + default="REJECT", + max_length=32, + verbose_name="policy for non members", + ), + ), + ( + "banned", + models.CharField( + choices=[ + ("REJECT", "Reject the machine"), + ("SET_VLAN", "Place the machine on the VLAN"), + ], + default="REJECT", + max_length=32, + verbose_name="policy for banned users", + ), + ), + ], + ), + migrations.CreateModel( + name="CotisationsOption", + bases=(re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "send_voucher_mail", + models.BooleanField( + verbose_name="send voucher by email when the invoice is controlled", + help_text="Be careful, if no mandate is defined on the preferences page, errors will be triggered when generating vouchers.", + default=False, + ), + ), + ], + ), + migrations.CreateModel( + name="DocumentTemplate", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "template", + models.FileField(upload_to="templates/", verbose_name="template"), + ), + ( + "name", + models.CharField(max_length=125, unique=True, verbose_name="name"), + ), + ], + options={ + "verbose_name": "document template", + "verbose_name_plural": "document templates", + }, + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + ), + ] From 6aa072f6d19b67e7d1d2b1d942a51a049c7631b3 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 14:31:08 +0100 Subject: [PATCH 450/490] Cotisations models definition. --- cotisations/migrations/0001_squashed_0050.py | 511 +++++++++++++++++++ 1 file changed, 511 insertions(+) create mode 100644 cotisations/migrations/0001_squashed_0050.py diff --git a/cotisations/migrations/0001_squashed_0050.py b/cotisations/migrations/0001_squashed_0050.py new file mode 100644 index 00000000..ebc02daa --- /dev/null +++ b/cotisations/migrations/0001_squashed_0050.py @@ -0,0 +1,511 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.core.validators +import re2o.mixins +import re2o.aes_field +import re2o.field_permissions +import cotisations.models +import cotisations.payment_methods.mixins + + +class Migration(migrations.Migration): + initial = True + dependencies = [] + replaces = [ + ("cotisations", "0001_initial.py"), + ("cotisations", "0002_remove_facture_article.py"), + ("cotisations", "0003_auto_20160702_1448.py"), + ("cotisations", "0004_auto_20160702_1528.py"), + ("cotisations", "0005_auto_20160702_1532.py"), + ("cotisations", "0006_auto_20160702_1534.py"), + ("cotisations", "0007_auto_20160702_1543.py"), + ("cotisations", "0008_auto_20160702_1614.py"), + ("cotisations", "0009_remove_cotisation_user.py"), + ("cotisations", "0010_auto_20160702_1840.py"), + ("cotisations", "0011_auto_20160702_1911.py"), + ("cotisations", "0012_auto_20160704_0118.py"), + ("cotisations", "0013_auto_20160711_2240.py"), + ("cotisations", "0014_auto_20160712_0245.py"), + ("cotisations", "0015_auto_20160714_2142.py"), + ("cotisations", "0016_auto_20160715_0110.py"), + ("cotisations", "0017_auto_20170718_2329.py"), + ("cotisations", "0018_paiement_type_paiement.py"), + ("cotisations", "0019_auto_20170819_0055.py"), + ("cotisations", "0020_auto_20170819_0057.py"), + ("cotisations", "0021_auto_20170819_0104.py"), + ("cotisations", "0022_auto_20170824_0128.py"), + ("cotisations", "0023_auto_20170902_1303.py"), + ("cotisations", "0024_auto_20171015_2033.py"), + ("cotisations", "0025_article_type_user.py"), + ("cotisations", "0026_auto_20171028_0126.py"), + ("cotisations", "0027_auto_20171029_1156.py"), + ("cotisations", "0028_auto_20171231_0007.py"), + ("cotisations", "0029_auto_20180414_2056.py"), + ("cotisations", "0030_custom_payment.py"), + ("cotisations", "0031_comnpaypayment_production.py"), + ("cotisations", "0032_custom_invoice.py"), + ("cotisations", "0033_auto_20180818_1319.py"), + ("cotisations", "0034_auto_20180831_1532.py"), + ("cotisations", "0035_notepayment.py"), + ("cotisations", "0036_custominvoice_remark.py"), + ("cotisations", "0037_costestimate.py"), + ("cotisations", "0038_auto_20181231_1657.py"), + ("cotisations", "0039_freepayment.py"), + ("cotisations", "0040_auto_20191002_2335.py"), + ("cotisations", "0041_auto_20191103_2131.py"), + ("cotisations", "0042_auto_20191120_0159.py"), + ("cotisations", "0043_separation_membership_connection_p1.py"), + ("cotisations", "0044_separation_membership_connection_p2.py"), + ("cotisations", "0045_separation_membership_connection_p3.py"), + ("cotisations", "0046_article_need_membership.py"), + ("cotisations", "0047_article_need_membership_init.py"), + ("cotisations", "0048_auto_20201017_0018.py"), + ("cotisations", "0049_auto_20201102_2305.py"), + ("cotisations", "0050_auto_20201102_2342.py"), + ("cotisations", "0051_auto_20201228_1636.py"), + ] + operations = [ + migrations.CreateModel( + name="BaseInvoice", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date", models.DateTimeField(auto_now_add=True, verbose_name="Date")), + ], + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + re2o.field_permissions.FieldPermissionModelMixin, + models.Model, + ), + ), + migrations.CreateModel( + name="Facture", + fields=[ + ( + "baseinvoice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.BaseInvoice", + ), + ), + ( + "cheque", + models.CharField( + max_length=255, blank=True, verbose_name="cheque number" + ), + ), + ("valid", models.BooleanField(default=False, verbose_name="validated")), + ( + "control", + models.BooleanField(default=False, verbose_name="controlled"), + ), + ], + options={ + "permissions": ( + ("change_facture_control", 'Can edit the "controlled" state'), + ("view_facture", "Can view an invoice object"), + ("change_all_facture", "Can edit all the previous invoices"), + ), + "verbose_name": "invoice", + "verbose_name_plural": "invoices", + }, + ), + migrations.CreateModel( + name="CustomInvoice", + fields=[ + ( + "baseinvoice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.BaseInvoice", + ), + ), + ( + "recipient", + models.CharField(max_length=255, verbose_name="Recipient"), + ), + ( + "payment", + models.CharField(max_length=255, verbose_name="Payment type"), + ), + ("address", models.CharField(max_length=255, verbose_name="Address")), + ("paid", models.BooleanField(verbose_name="Paid")), + ( + "remark", + models.TextField(verbose_name="remark", blank=True, null=True), + ), + ], + bases=("cotisations.baseinvoice",), + options={ + "permissions": (("view_custominvoice", "Can view a custom invoice"),) + }, + ), + migrations.CreateModel( + name="CostEstimate", + fields=[ + ( + "custominvoice_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="cotisations.CustomInvoice", + ), + ), + ( + "validity", + models.DurationField( + verbose_name="Period of validity", help_text="DD HH:MM:SS" + ), + ), + ], + options={ + "permissions": ( + ("view_costestimate", "Can view a cost estimate object"), + ) + }, + bases=("cotisations.custominvoice",), + ), + migrations.CreateModel( + name="Vente", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "number", + models.IntegerField( + validators=[django.core.validators.MinValueValidator(1)], + verbose_name="amount", + ), + ), + ("name", models.CharField(max_length=255, verbose_name="article")), + ( + "prix", + models.DecimalField( + max_digits=5, decimal_places=2, verbose_name="price" + ), + ), + ( + "duration_connection", + models.PositiveIntegerField( + default=0, verbose_name="duration of the connection (in months)" + ), + ), + ( + "duration_days_connection", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration of the connection (in days, will be added to duration in months)", + ), + ), + ( + "duration_membership", + models.PositiveIntegerField( + default=0, verbose_name="duration of the membership (in months)" + ), + ), + ( + "duration_days_membership", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration of the membership (in days, will be added to duration in months)", + ), + ), + ], + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + options={ + "permissions": ( + ("view_vente", "Can view a purchase object"), + ("change_all_vente", "Can edit all the previous purchases"), + ), + "verbose_name": "purchase", + "verbose_name_plural": "purchases", + }, + ), + migrations.CreateModel( + name="Article", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField(max_length=255, verbose_name="designation"), + ), + ( + "prix", + models.DecimalField( + max_digits=5, decimal_places=2, verbose_name="unit price" + ), + ), + ( + "duration_connection", + models.PositiveIntegerField( + default=0, verbose_name="duration of the connection (in months)" + ), + ), + ( + "duration_days_connection", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration of the connection (in days, will be added to duration in months)", + ), + ), + ( + "duration_membership", + models.PositiveIntegerField( + default=0, verbose_name="duration of the membership (in months)" + ), + ), + ( + "duration_days_membership", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MinValueValidator(0)], + verbose_name="duration of the membership (in days, will be added to duration in months)", + ), + ), + ( + "need_membership", + models.BooleanField( + default=True, verbose_name="need membership to be purchased" + ), + ), + ("type_user", models.CharField( + choices=[ + ("Adherent", "Member"), + ("Club", "Club"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="type of users concerned", + )), + ("available_for_everyone", models.BooleanField(default=False, verbose_name="is available for every user")), + + ], + options={ + "permissions":(("view_article", "Can view an article object"),("buy_every_article", "Can buy every article")), + "verbose_name":"article", + "verbose_name_plural":"articles" + } + ), + migrations.CreateModel( + name="Banque", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)) + ], + options={"permissions": (("view_banque", "Can view a bank object"),), "verbose_name":"bank", "verbose_name_plural":"banks"} + ), + migrations.CreateModel( + name="Paiement", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("moyen", models.CharField(max_length=255, verbose_name="method")), + ("available_for_everyone", models.BooleanField(default=False, verbose_name="is available for every user",)), + ("is_balance", models.BooleanField(default=False,editable=False, verbose_name="is user balance",help_text="There should be only one balance payment method.",validators=[cotisations.models.check_no_balance])) + ], + options={ + "permissions":(("view_paiement", "Can view a payment method object"), ("use_every_payment", "Can use every payment method")), + "verbose_name": "payment method", + "verbose_name_plural": "payment methods" + } + ), + migrations.CreateModel( + name="Cotisation", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("date_start_con", models.DateTimeField(verbose_name="start date for the connection")), + ("date_end_con", models.DateTimeField(verbose_name="end date for the connection")), + ("date_start_memb", models.DateTimeField(verbose_name="start date for the membership")), + ("date_end_memb", models.DateTimeField(verbose_name="end date for the membership")) + + ], + options={ + "permissions":( ("view_cotisation", "Can view a subscription object"),("change_all_cotisation", "Can edit the previous subscriptions")), + "verbose_name":"subscription", + "verbose_name_plural":"subscriptions" + } + ), + migrations.CreateModel( + name="BalancePayment", + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("minimum_balance", models.DecimalField(verbose_name="minimum balance",help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.", max_digits=5, decimal_places=2, default=0)), + ("maximum_balance", models.DecimalField(verbose_name="maximum balance", help_text="The maximal amount of money allowed for the balance.", max_digits=5, decimal_places=2, default=50, blank=True, null=True)), + ("credit_balance_allowed", models.BooleanField(verbose_name="allow user to credit their balance", default=False)) + + ], + options={"verbose_name", "user balance"} + ), + migrations.CreateModel( + name="ChequePayment", + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ], + options={"verbose_name", "cheque"} + ), + migrations.CreateModel( + name="ComnpayPayment", + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("payment_credential", models.CharField(max_length=255, default="", blank=True, verbose_name="ComNpay VAT Number")), + ("payment_pass", re2o.aes_field.AESEncryptedField(max_length=255, null=True, blank=True, verbose_name="ComNpay secret key")), + ("minimum_payment", models.DecimalField(verbose_name="minimum payment", help_text="The minimal amount of money you have to use when paying with ComNpay.", max_digits=5,decimal_places=2,default=1)), + ("production", models.BooleanField(default=True, verbose_name="production mode enabled (production URL, instead of homologation)")) + ], + options={"verbose_name", "ComNpay"} + ), + migrations.CreateModel( + name="FreePayment", + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ], + options={"verbose_name", "Free payment"} + ), + migrations.CreateModel( + name="NotePayment", + bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("server", models.CharField(max_length=255, verbose_name="server")), + ("port", models.PositiveIntegerField(blank=True, null=True)), + ("id_note", models.PositiveIntegerField(blank=True, null=True)) + ], + options={"verbose_name", "NoteKfet"} + ), + ] + + + + From 9eef45678f5d1926541b3b96e431b5b41c82565d Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 28 Dec 2020 21:35:57 +0100 Subject: [PATCH 451/490] Machines models creation. --- machines/migrations/0001_squashed_0108.py | 1040 +++++++++++++++++++++ 1 file changed, 1040 insertions(+) create mode 100644 machines/migrations/0001_squashed_0108.py diff --git a/machines/migrations/0001_squashed_0108.py b/machines/migrations/0001_squashed_0108.py new file mode 100644 index 00000000..db54d296 --- /dev/null +++ b/machines/migrations/0001_squashed_0108.py @@ -0,0 +1,1040 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import django.core.validators +import macaddress.fields +import re2o.mixins +import re2o.field_permissions +import datetime + + +class Migration(migrations.Migration): + initial = True + dependencies = [] + replaces = [ + ("machines", "0001_initial.py"), + ("machines", "0001_squashed_0108.py"), + ("machines", "0002_auto_20160703_1444.py"), + ("machines", "0003_auto_20160703_1450.py"), + ("machines", "0004_auto_20160703_1451.py"), + ("machines", "0005_auto_20160703_1523.py"), + ("machines", "0006_auto_20160703_1813.py"), + ("machines", "0007_auto_20160703_1816.py"), + ("machines", "0008_remove_interface_ipv6.py"), + ("machines", "0009_auto_20160703_2358.py"), + ("machines", "0010_auto_20160704_0104.py"), + ("machines", "0011_auto_20160704_0105.py"), + ("machines", "0012_auto_20160704_0118.py"), + ("machines", "0013_auto_20160705_1014.py"), + ("machines", "0014_auto_20160706_1220.py"), + ("machines", "0015_auto_20160707_0105.py"), + ("machines", "0016_auto_20160708_1633.py"), + ("machines", "0017_auto_20160708_1645.py"), + ("machines", "0018_auto_20160708_1813.py"), + ("machines", "0019_auto_20160718_1141.py"), + ("machines", "0020_auto_20160718_1849.py"), + ("machines", "0021_auto_20161006_1943.py"), + ("machines", "0022_auto_20161011_1829.py"), + ("machines", "0023_iplist_ip_type.py"), + ("machines", "0024_machinetype_need_infra.py"), + ("machines", "0025_auto_20161023_0038.py"), + ("machines", "0026_auto_20161026_1348.py"), + ("machines", "0027_alias.py"), + ("machines", "0028_iptype_domaine_ip.py"), + ("machines", "0029_iptype_domaine_range.py"), + ("machines", "0030_auto_20161118_1730.py"), + ("machines", "0031_auto_20161119_1709.py"), + ("machines", "0032_auto_20161119_1850.py"), + ("machines", "0033_extension_need_infra.py"), + ("machines", "0034_iplist_need_infra.py"), + ("machines", "0035_auto_20161224_1201.py"), + ("machines", "0036_auto_20161224_1204.py"), + ("machines", "0037_domain_cname.py"), + ("machines", "0038_auto_20161224_1721.py"), + ("machines", "0039_auto_20161224_1732.py"), + ("machines", "0040_remove_interface_dns.py"), + ("machines", "0041_remove_ns_interface.py"), + ("machines", "0042_ns_ns.py"), + ("machines", "0043_auto_20170721_0350.py"), + ("machines", "0044_auto_20170808_0233.py"), + ("machines", "0045_auto_20170808_0348.py"), + ("machines", "0046_auto_20170808_1423.py"), + ("machines", "0047_auto_20170809_0606.py"), + ("machines", "0048_auto_20170823_2315.py"), + ("machines", "0049_vlan.py"), + ("machines", "0050_auto_20170826_0022.py"), + ("machines", "0051_iptype_vlan.py"), + ("machines", "0052_auto_20170828_2322.py"), + ("machines", "0053_text.py"), + ("machines", "0054_text_zone.py"), + ("machines", "0055_nas.py"), + ("machines", "0056_nas_port_access_mode.py"), + ("machines", "0057_nas_autocapture_mac.py"), + ("machines", "0058_auto_20171002_0350.py"), + ("machines", "0059_iptype_prefix_v6.py"), + ("machines", "0060_iptype_ouverture_ports.py"), + ("machines", "0061_auto_20171015_2033.py"), + ("machines", "0062_extension_origin_v6.py"), + ("machines", "0063_auto_20171020_0040.py"), + ("machines", "0064_auto_20171115_0253.py"), + ("machines", "0065_auto_20171115_1514.py"), + ("machines", "0066_srv.py"), + ("machines", "0067_auto_20171116_0152.py"), + ("machines", "0068_auto_20171116_0252.py"), + ("machines", "0069_auto_20171116_0822.py"), + ("machines", "0070_auto_20171231_1947.py"), + ("machines", "0071_auto_20171231_2100.py"), + ("machines", "0072_auto_20180108_1822.py"), + ("machines", "0073_auto_20180128_2203.py"), + ("machines", "0074_auto_20180129_0352.py"), + ("machines", "0075_auto_20180130_0052.py"), + ("machines", "0076_auto_20180130_1623.py"), + ("machines", "0077_auto_20180409_2243.py"), + ("machines", "0078_auto_20180415_1252.py"), + ("machines", "0079_auto_20180416_0107.py"), + ("machines", "0080_auto_20180502_2334.py"), + ("machines", "0081_auto_20180521_1413.py"), + ("machines", "0082_auto_20180525_2209.py"), + ("machines", "0083_remove_duplicate_rights.py"), + ("machines", "0084_dname.py"), + ("machines", "0085_sshfingerprint.py"), + ("machines", "0086_role.py"), + ("machines", "0087_dnssec.py"), + ("machines", "0088_iptype_prefix_v6_length.py"), + ("machines", "0089_auto_20180805_1148.py"), + ("machines", "0090_auto_20180805_1459.py"), + ("machines", "0091_auto_20180806_2310.py"), + ("machines", "0092_auto_20180807_0926.py"), + ("machines", "0093_auto_20180807_1115.py"), + ("machines", "0094_auto_20180815_1918.py"), + ("machines", "0095_auto_20180919_2225.py"), + ("machines", "0096_auto_20181013_1417.py"), + ("machines", "0097_extension_dnssec.py"), + ("machines", "0098_auto_20190102_1745.py"), + ("machines", "0099_role_recursive_dns.py"), + ("machines", "0100_auto_20190102_1753.py"), + ("machines", "0101_auto_20190108_1623.py"), + ("machines", "0102_auto_20190303_1611.py"), + ("machines", "0103_auto_20191002_2222.py"), + ("machines", "0104_auto_20191002_2231.py"), + ("machines", "0105_dname_ttl.py"), + ("machines", "0106_auto_20191120_0159.py"), + ("machines", "0107_fix_lowercase_domain.py"), + ("machines", "0108_ipv6list_active.py"), + ] + operations = [ + migrations.CreateModel( + name="Machine", + bases=( + re2o.mixins.RevMixin, + re2o.field_permissions.FieldPermissionModelMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, help_text="Optional.", blank=True, null=True + ), + ), + ("active", models.BooleanField(default=True)), + ], + options={ + "permissions": ( + ("view_machine", "Can view a machine object"), + ("change_machine_user", "Can change the user of a machine"), + ), + "verbose_name": "machine", + "verbose_name_plural": "machines", + }, + ), + migrations.CreateModel( + name="MachineType", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": ( + ("view_machinetype", "Can view a machine type object"), + ("use_all_machinetype", "Can use all machine types"), + ), + "verbose_name": "machine type", + "verbose_name_plural": "machine types", + }, + ), + migrations.CreateModel( + name="IpType", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("need_infra", models.BooleanField(default=False)), + ("domaine_ip_start", models.GenericIPAddressField(protocol="IPv4")), + ("domaine_ip_stop", models.GenericIPAddressField(protocol="IPv4")), + ( + "domaine_ip_network", + models.GenericIPAddressField( + protocol="IPv4", + null=True, + blank=True, + help_text="Network containing the domain's IPv4 range optional).", + ), + ), + ( + "domaine_ip_netmask", + models.IntegerField( + default=24, + validators=[ + django.core.validators.MaxValueValidator(31), + django.core.validators.MinValueValidator(8), + ], + help_text="Netmask for the domain's IPv4 range.", + ), + ), + ( + "reverse_v4", + models.BooleanField( + default=False, help_text="Enable reverse DNS for IPv4." + ), + ), + ( + "prefix_v6", + models.GenericIPAddressField( + protocol="IPv6", null=True, blank=True + ), + ), + ( + "prefix_v6_length", + models.IntegerField( + default=64, + validators=[ + django.core.validators.MaxValueValidator(128), + django.core.validators.MinValueValidator(0), + ], + ), + ), + ( + "reverse_v6", + models.BooleanField( + default=False, help_text="Enable reverse DNS for IPv6." + ), + ), + ], + options={ + "permissions": ( + ("view_iptype", "Can view an IP type object"), + ("use_all_iptype", "Can use all IP types"), + ), + "verbose_name": "Ip type", + "verbose_name_plural": "Ip types", + }, + ), + migrations.CreateModel( + name="Vlan", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "vlan_id", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(4095)] + ), + ), + ("name", models.CharField(max_length=256)), + ("comment", models.CharField(max_length=256, blank=True)), + ("arp_protect", models.BooleanField(default=False)), + ("dhcp_snooping", models.BooleanField(default=False)), + ("dhcpv6_snooping", models.BooleanField(default=False)), + ( + "igmp", + models.BooleanField( + default=False, help_text="v4 multicast management." + ), + ), + ( + "mld", + models.BooleanField( + default=False, help_text="v6 multicast management." + ), + ), + ], + options={ + "permissions": (("view_vlan", "Can view a VLAN object"),), + "verbose_name": "VLAN", + "verbose_name_plural": "VLANs", + }, + ), + migrations.CreateModel( + name="Nas", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255, unique=True)), + ( + "port_access_mode", + models.CharField( + choices=(("802.1X", "802.1X"), ("Mac-address", "MAC-address")), + default="802.1X", + max_length=32, + ), + ), + ("autocapture_mac", models.BooleanField(default=False)), + ], + options={ + "permissions": (("view_nas", "Can view a NAS device object"),), + "verbose_name": "NAS device", + "verbose_name_plural": "NAS devices", + }, + ), + migrations.CreateModel( + name="SOA", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ( + "mail", + models.EmailField(help_text="Contact email address for the zone."), + ), + ( + "refresh", + models.PositiveIntegerField( + default=86400, + help_text="Seconds before the secondary DNS have to ask the primary DNS serial to detect a modification.", + ), + ), + ( + "retry", + models.PositiveIntegerField( + default=7200, + help_text="Seconds before the secondary DNS ask the serial again in case of a primary DNS timeout.", + ), + ), + ( + "expire", + models.PositiveIntegerField( + default=3600000, + help_text="Seconds before the secondary DNS stop answering requests in case of primary DNS timeout.", + ), + ), + ( + "ttl", + models.PositiveIntegerField( + default=172800, help_text="Time To Live." + ), + ), + ], + options={ + "permissions": (("view_soa", "Can view an SOA record object"),), + "verbose_name": "SOA record", + "verbose_name_plural": "SOA records", + }, + ), + migrations.CreateModel( + name="Extension", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField( + max_length=255, + unique=True, + help_text="Zone name, must begin with a dot (.example.org).", + ), + ), + ("need_infra", models.BooleanField(default=False)), + ( + "origin_v6", + models.GenericIPAddressField( + protocol="IPv6", + null=True, + blank=True, + help_text="AAAA record associated with the zone.", + ), + ), + ( + "dnssec", + models.BooleanField( + default=False, + help_text="Should the zone be signed with DNSSEC.", + ), + ), + ], + options={ + "permissions": ( + ("view_extension", "Can view an extension object"), + ("use_all_extension", "Can use all extensions"), + ), + "verbose_name": "DNS extension", + "verbose_name_plural": "DNS extensions", + }, + ), + migrations.CreateModel( + name="Mx", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("priority", models.PositiveIntegerField()), + ( + "ttl", + models.PositiveIntegerField( + verbose_name="Time To Live (TTL)", default=172800 + ), + ), + ], + options={ + "permissions": (("view_mx", "Can view an MX record object"),), + "verbose_name": "MX record", + "verbose_name_plural": "MX records", + }, + ), + migrations.CreateModel( + name="Ns", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "ttl", + models.PositiveIntegerField( + verbose_name="Time To Live (TTL)", default=172800 + ), + ), + ], + options={ + "permissions": (("view_ns", "Can view an NS record object"),), + "verbose_name": "NS record", + "verbose_name_plural": "NS records", + }, + ), + migrations.CreateModel( + name="Txt", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("field1", models.CharField(max_length=255)), + ("field2", models.TextField(max_length=2047)), + ( + "ttl", + models.PositiveIntegerField( + verbose_name="Time To Live (TTL)", default=172800 + ), + ), + ], + options={ + "permissions": (("view_txt", "Can view a TXT record object"),), + "verbose_name": "TXT record", + "verbose_name_plural": "TXT records", + }, + ), + migrations.CreateModel( + name="DName", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("alias", models.CharField(max_length=255)), + ( + "ttl", + models.PositiveIntegerField( + verbose_name="Time To Live (TTL)", default=172800 + ), + ), + ], + options={ + "permissions": (("view_dname", "Can view a DNAME record object"),), + "verbose_name": "DNAME record", + "verbose_name_plural": "DNAME records", + }, + ), + migrations.CreateModel( + name="Srv", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("service", models.CharField(max_length=31)), + ( + "protocole", + models.CharField( + max_length=3, + choices=(("TCP", "TCP"), ("UDP", "UDP")), + default="TCP", + ), + ), + ( + "ttl", + models.PositiveIntegerField( + default=172800, help_text="Time To Live." + ), + ), + ( + "priority", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MaxValueValidator(65535)], + help_text="Priority of the target server (positive integer value, the lower it is, the more the server will be used if available).", + ), + ), + ( + "weight", + models.PositiveIntegerField( + default=0, + validators=[django.core.validators.MaxValueValidator(65535)], + help_text="Relative weight for records with the same priority (integer value between 0 and 65535).", + ), + ), + ( + "port", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)], + help_text="TCP/UDP port.", + ), + ), + ], + options={ + "permissions": (("view_srv", "Can view an SRV record object"),), + "verbose_name": "SRV record", + "verbose_name_plural": "SRV records", + }, + ), + migrations.CreateModel( + name="SshFp", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "pub_key_entry", + models.TextField(help_text="SSH public key.", max_length=2048), + ), + ( + "algo", + models.CharField( + choices=( + ("ssh-rsa", "ssh-rsa"), + ("ssh-ed25519", "ssh-ed25519"), + ("ecdsa-sha2-nistp256", "ecdsa-sha2-nistp256"), + ("ecdsa-sha2-nistp384", "ecdsa-sha2-nistp384"), + ("ecdsa-sha2-nistp521", "ecdsa-sha2-nistp521"), + ), + max_length=32, + ), + ), + ( + "comment", + models.CharField( + help_text="Comment.", max_length=255, null=True, blank=True + ), + ), + ], + options={ + "permissions": (("view_sshfp", "Can view an SSHFP record object"),), + "verbose_name": "SSHFP record", + "verbose_name_plural": "SSHFP records", + }, + ), + migrations.CreateModel( + name="Interface", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + re2o.field_permissions.FieldPermissionModelMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("mac_address", macaddress.fields.MACAddressField(integer=False)), + ("details", models.CharField(max_length=255, blank=True)), + ], + options={ + "permissions": ( + ("view_interface", "Can view an interface object"), + ( + "change_interface_machine", + "Can change the owner of an interface", + ), + ), + "verbose_name": "interface", + "verbose_name_plural": "interfaces", + }, + ), + migrations.CreateModel( + name="Ipv6List", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + re2o.field_permissions.FieldPermissionModelMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("ipv6", models.GenericIPAddressField(protocol="IPv6")), + ("slaac_ip", models.BooleanField(default=False)), + ( + "active", + models.BooleanField( + default=True, + help_text="If false,the DNS will not provide this ip.", + ), + ), + ], + options={ + "permissions": ( + ("view_ipv6list", "Can view an IPv6 addresses list object"), + ( + "change_ipv6list_slaac_ip", + "Can change the SLAAC value of an IPv6 addresses list", + ), + ), + "verbose_name": "IPv6 addresses list", + "verbose_name_plural": "IPv6 addresses lists", + }, + ), + migrations.CreateModel( + name="Domain", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + re2o.field_permissions.FieldPermissionModelMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField( + help_text="Mandatory and unique, must not contain dots.", + max_length=255, + ), + ), + ( + "cname", + models.ForeignKey( + "self", null=True, blank=True, related_name="related_domain" + ), + ), + ( + "ttl", + models.PositiveIntegerField( + verbose_name="Time To Live (TTL)", default=0 + ), + ), + ], + options={ + "unique_together": (("name", "extension"),), + "permissions": ( + ("view_domain", "Can view a domain object"), + ("change_ttl", "Can change the TTL of a domain object"), + ), + "verbose_name": "domain", + "verbose_name_plural": "domains", + }, + ), + migrations.CreateModel( + name="IpList", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("ipv4", models.GenericIPAddressField(protocol="IPv4", unique=True)), + ], + options={ + "permissions": ( + ("view_iplist", "Can view an IPv4 addresses list object"), + ), + "verbose_name": "IPv4 addresses list", + "verbose_name_plural": "IPv4 addresses lists", + }, + ), + migrations.CreateModel( + name="Role", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("role_type", models.CharField(max_length=255, unique=True)), + ( + "specific_role", + models.CharField( + choices=( + ("dhcp-server", "DHCP server"), + ("switch-conf-server", "Switches configuration server"), + ("dns-recursive-server", "Recursive DNS server"), + ("ntp-server", "NTP server"), + ("radius-server", "RADIUS server"), + ("log-server", "Log server"), + ("ldap-master-server", "LDAP master server"), + ("ldap-backup-server", "LDAP backup server"), + ("smtp-server", "SMTP server"), + ("postgresql-server", "postgreSQL server"), + ("mysql-server", "mySQL server"), + ("sql-client", "SQL client"), + ("gateway", "Gateway"), + ), + null=True, + blank=True, + max_length=32, + ), + ), + ], + options={ + "permissions": (("view_role", "Can view a role object"),), + "verbose_name": "server role", + "verbose_name_plural": "server roles", + }, + ), + migrations.CreateModel( + name="Service", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "service_type", + models.CharField(max_length=255, blank=True, unique=True), + ), + ( + "min_time_regen", + models.DurationField( + default=datetime.timedelta(minutes=1), + help_text="Minimal time before regeneration of the service.", + ), + ), + ( + "regular_time_regen", + models.DurationField( + default=datetime.timedelta(hours=1), + help_text="Maximal time before regeneration of the service.", + ), + ), + ], + options={ + "permissions": (("view_service", "Can view a service object"),), + "verbose_name": "service to generate (DHCP, DNS, ...)", + "verbose_name_plural": "services to generate (DHCP, DNS, ...)", + }, + ), + migrations.CreateModel( + name="Service_link", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("last_regen", models.DateTimeField(auto_now_add=True)), + ("asked_regen", models.BooleanField(default=False)), + ], + options={ + "permissions": ( + ("view_service_link", "Can view a service server link object"), + ), + "verbose_name": "link between service and server", + "verbose_name_plural": "links between service and server", + }, + ), + migrations.CreateModel( + name="OuverturePortList", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField( + help_text="Name of the ports configuration", max_length=255 + ), + ), + ], + options={ + "permissions": ( + ( + "view_ouvertureportlist", + "Can view a ports opening list" " object", + ), + ), + "verbose_name": "ports opening list", + "verbose_name_plural": "ports opening lists", + }, + ), + migrations.CreateModel( + name="OuverturePort", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "begin", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), + ), + ( + "end", + models.PositiveIntegerField( + validators=[django.core.validators.MaxValueValidator(65535)] + ), + ), + ( + "protocole", + models.CharField( + max_length=1, choices=(("T", "TCP"), ("U", "UDP")), default="T" + ), + ), + ( + "io", + models.CharField( + max_length=1, choices=(("I", "IN"), ("O", "OUT")), default="O" + ), + ), + ], + options={ + "verbose_name": "ports opening", + "verbose_name_plural": "ports openings", + }, + ), + ] From 9b1f06eae5733699be907cb790bb00e4f64b84f0 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 29 Dec 2020 14:49:48 +0100 Subject: [PATCH 452/490] Users models creation. --- users/migrations/0001_squashed_0095.py | 543 +++++++++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 users/migrations/0001_squashed_0095.py diff --git a/users/migrations/0001_squashed_0095.py b/users/migrations/0001_squashed_0095.py new file mode 100644 index 00000000..4d426438 --- /dev/null +++ b/users/migrations/0001_squashed_0095.py @@ -0,0 +1,543 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +from django.conf import settings +import django.contrib.auth.models +import django.core.validators +import re2o.mixins +import re2o.field_permissions +import users.models + + +class Migration(migrations.Migration): + initial = True + dependencies = [] + replaces = [ + ("users", "0001_initial.py"), + ("users", "0002_auto_20160630_2301.py"), + ("users", "0003_listrights_rights.py"), + ("users", "0004_auto_20160701_2312.py"), + ("users", "0005_auto_20160702_0006.py"), + ("users", "0006_ban.py"), + ("users", "0007_auto_20160702_2322.py"), + ("users", "0008_user_registered.py"), + ("users", "0009_user_room.py"), + ("users", "0010_auto_20160703_1226.py"), + ("users", "0011_auto_20160703_1227.py"), + ("users", "0012_auto_20160703_1230.py"), + ("users", "0013_auto_20160704_1547.py"), + ("users", "0014_auto_20160704_1548.py"), + ("users", "0015_whitelist.py"), + ("users", "0016_auto_20160706_1220.py"), + ("users", "0017_auto_20160707_0105.py"), + ("users", "0018_auto_20160707_0115.py"), + ("users", "0019_auto_20160708_1633.py"), + ("users", "0020_request.py"), + ("users", "0021_ldapuser.py"), + ("users", "0022_ldapuser_sambasid.py"), + ("users", "0023_auto_20160724_1908.py"), + ("users", "0024_remove_ldapuser_mac_list.py"), + ("users", "0025_listshell.py"), + ("users", "0026_user_shell.py"), + ("users", "0027_auto_20160726_0216.py"), + ("users", "0028_auto_20160726_0227.py"), + ("users", "0029_auto_20160726_0229.py"), + ("users", "0030_auto_20160726_0357.py"), + ("users", "0031_auto_20160726_0359.py"), + ("users", "0032_auto_20160727_2122.py"), + ("users", "0033_remove_ldapuser_loginshell.py"), + ("users", "0034_auto_20161018_0037.py"), + ("users", "0035_auto_20161018_0046.py"), + ("users", "0036_auto_20161022_2146.py"), + ("users", "0037_auto_20161028_1906.py"), + ("users", "0038_auto_20161031_0258.py"), + ("users", "0039_auto_20161119_0033.py"), + ("users", "0040_auto_20161119_1709.py"), + ("users", "0041_listright_details.py"), + ("users", "0042_auto_20161126_2028.py"), + ("users", "0043_auto_20161224_1156.py"), + ("users", "0043_ban_state.py"), + ("users", "0044_user_ssh_public_key.py"), + ("users", "0045_merge.py"), + ("users", "0046_auto_20170617_1433.py"), + ("users", "0047_auto_20170618_0156.py"), + ("users", "0048_auto_20170618_0210.py"), + ("users", "0049_auto_20170618_1424.py"), + ("users", "0050_serviceuser_comment.py"), + ("users", "0051_user_telephone.py"), + ("users", "0052_ldapuser_shadowexpire.py"), + ("users", "0053_auto_20170626_2105.py"), + ("users", "0054_auto_20170626_2219.py"), + ("users", "0055_auto_20171003_0556.py"), + ("users", "0056_auto_20171015_2033.py"), + ("users", "0057_auto_20171023_0301.py"), + ("users", "0058_auto_20171025_0154.py"), + ("users", "0059_auto_20171025_1854.py"), + ("users", "0060_auto_20171120_0317.py"), + ("users", "0061_auto_20171230_2033.py"), + ("users", "0062_auto_20171231_0056.py"), + ("users", "0063_auto_20171231_0140.py"), + ("users", "0064_auto_20171231_0150.py"), + ("users", "0065_auto_20171231_2053.py"), + ("users", "0066_grouppermissions.py"), + ("users", "0067_serveurpermission.py"), + ("users", "0068_auto_20180107_2245.py"), + ("users", "0069_club_mailing.py"), + ("users", "0070_auto_20180324_1906.py"), + ("users", "0071_auto_20180415_1252.py"), + ("users", "0072_auto_20180426_2021.py"), + ("users", "0073_auto_20180629_1614.py"), + ("users", "0074_auto_20180810_2104.py"), + ("users", "0074_auto_20180814_1059.py"), + ("users", "0075_merge_20180815_2202.py"), + ("users", "0076_auto_20180818_1321.py"), + ("users", "0077_auto_20180824_1750.py"), + ("users", "0078_auto_20181011_1405.py"), + ("users", "0079_auto_20181228_2039.py"), + ("users", "0080_auto_20190108_1726.py"), + ("users", "0081_auto_20190317_0302.py"), + ("users", "0082_auto_20190908_1338.py"), + ("users", "0083_user_shortcuts_enabled.py"), + ("users", "0084_auto_20191120_0159.py"), + ("users", "0085_user_email_state.py"), + ("users", "0086_user_email_change_date.py"), + ("users", "0087_request_email.py"), + ("users", "0088_auto_20200417_2312.py"), + ("users", "0089_auto_20200418_0112.py"), + ("users", "0090_auto_20200421_1825.py"), + ("users", "0091_auto_20200423_1256.py"), + ("users", "0092_auto_20200502_0057.py"), + ("users", "0093_user_profile_image.py"), + ("users", "0094_remove_user_profile_image.py"), + ("users", "0095_user_theme.py"), + ] + operations = [ + migrations.CreateModel( + name="User", + bases=( + re2o.mixins.RevMixin, + re2o.field_permissions.FieldPermissionModelMixin, + django.contrib.auth.models.AbstractBaseUser, + django.contrib.auth.models.PermissionsMixin, + re2o.mixins.AclMixin, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("surname", models.CharField(max_length=255)), + ( + "pseudo", + models.CharField( + max_length=32, + unique=True, + help_text="Must only contain letters, numerals or dashes.", + validators=[users.models.linux_user_validator], + ), + ), + ( + "email", + models.EmailField( + blank=True, + default="", + help_text="External email address allowing us to contact you.", + ), + ), + ( + "local_email_redirect", + models.BooleanField( + default=False, + help_text="Enable redirection of the local email messages to the main email address.", + ), + ), + ( + "local_email_enabled", + models.BooleanField( + default=False, help_text="Enable the local email account." + ), + ), + ( + "comment", + models.CharField( + help_text="Comment, school year.", max_length=255, blank=True + ), + ), + ("pwd_ntlm", models.CharField(max_length=255)), + ( + "state", + models.IntegerField( + choices=( + (0, "Active"), + (1, "Disabled"), + (2, "Archived"), + (3, "Not yet active"), + (4, "Fully archived"), + ), + default=3, + help_text="Account state.", + ), + ), + ( + "email_state", + models.IntegerField( + choices=( + (0, "Confirmed"), + (1, "Not confirmed"), + (2, "Waiting for email confirmation"), + ), + default=2, + ), + ), + ("registered", models.DateTimeField(auto_now_add=True)), + ("telephone", models.CharField(max_length=15, blank=True, null=True)), + ( + "uid_number", + models.PositiveIntegerField( + default=users.models.get_fresh_user_uid, unique=True + ), + ), + ( + "legacy_uid", + models.PositiveIntegerField( + unique=True, + blank=True, + null=True, + help_text="Optionnal legacy uid, for import and transition purpose", + ), + ), + ( + "shortcuts_enabled", + models.BooleanField( + verbose_name="enable shortcuts on Re2o website", default=True + ), + ), + ("email_change_date", models.DateTimeField(auto_now_add=True)), + ("theme", models.CharField(max_length=255, default="default.css")), + ], + options={ + "permissions": ( + ("change_user_password", "Can change the password of a user"), + ("change_user_state", "Can edit the state of a user"), + ("change_user_force", "Can force the move"), + ("change_user_shell", "Can edit the shell of a user"), + ("change_user_pseudo", "Can edit the pseudo of a user"), + ( + "change_user_groups", + "Can edit the groups of rights of a user (critical permission)", + ), + ( + "change_all_users", + "Can edit all users, including those with rights", + ), + ("view_user", "Can view a user object"), + ), + "verbose_name": "user (member or club)", + "verbose_name_plural": "users (members or clubs)", + }, + ), + migrations.CreateModel( + name="Adherent", + fields=[ + ( + "user_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + ("name", models.CharField(max_length=255)), + ( + "gpg_fingerprint", + models.CharField(max_length=49, blank=True, null=True), + ), + ], + options={"verbose_name": "member", "verbose_name_plural": "members"}, + ), + migrations.CreateModel( + name="Club", + fields=[ + ( + "user_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to=settings.AUTH_USER_MODEL, + ), + ), + ("mailing", models.BooleanField(default=False)), + ], + options={"verbose_name": "club", "verbose_name_plural": "clubs"}, + ), + migrations.CreateModel( + name="ServiceUser", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + django.contrib.auth.models.AbstractBaseUser, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "pseudo", + models.CharField( + max_length=32, + unique=True, + help_text="Must only contain letters, numerals or dashes.", + validators=[users.models.linux_user_validator], + ), + ), + ( + "access_group", + models.CharField( + choices=( + ("auth", "auth"), + ("readonly", "readonly"), + ("usermgmt", "usermgmt"), + ), + default="readonly", + max_length=32, + ), + ), + ( + "comment", + models.CharField(help_text="Comment.", max_length=255, blank=True), + ), + ], + options={ + "permissions": ( + ("view_serviceuser", "Can view a service user object"), + ), + "verbose_name": "service user", + "verbose_name_plural": "service users", + }, + ), + migrations.CreateModel( + name="School", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": (("view_school", "Can view a school object"),), + "verbose_name": "school", + "verbose_name_plural": "schools", + }, + ), + migrations.CreateModel( + name="ListRight", + bases=( + re2o.mixins.RevMixin, + re2o.mixins.AclMixin, + django.contrib.auth.models.Group, + ), + fields=[ + ( + "id", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="auth.Group", + ), + ), + ( + "unix_name", + models.CharField( + max_length=255, + unique=True, + validators=[ + django.core.validators.RegexValidator( + "^[a-z]+$", + message=( + "UNIX group names can only contain lower case letters." + ), + ) + ], + ), + ), + ("gid", models.PositiveIntegerField(unique=True, null=True)), + ("critical", models.BooleanField(default=False)), + ( + "details", + models.CharField( + help_text="Description.", max_length=255, blank=True + ), + ), + ], + options={ + "permissions": ( + ("view_listright", "Can view a group of rights object"), + ), + "verbose_name": "group of rights", + "verbose_name_plural": "groups of rights", + }, + ), + migrations.CreateModel( + name="ListShell", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("shell", models.CharField(max_length=255, unique=True)), + ], + options={ + "permissions": (("view_listshell", "Can view a shell object"),), + "verbose_name": "shell", + "verbose_name_plural": "shells", + }, + ), + migrations.CreateModel( + name="Ban", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("raison", models.CharField(max_length=255)), + ("date_start", models.DateTimeField(auto_now_add=True)), + ("date_end", models.DateTimeField()), + ( + "state", + models.IntegerField( + choices=( + (0, "HARD (no access)"), + (1, "SOFT (local access only)"), + (2, "RESTRICTED (speed limitation)"), + ), + default=0, + ), + ), + ], + options={ + "permissions": (("view_ban", "Can view a ban object"),), + "verbose_name": "ban", + "verbose_name_plural": "bans", + }, + ), + migrations.CreateModel( + name="Whitelist", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("raison", models.CharField(max_length=255)), + ("date_start", models.DateTimeField(auto_now_add=True)), + ("date_end", models.DateTimeField()), + ], + options={ + "permissions": (("view_whitelist", "Can view a whitelist object"),), + "verbose_name": "whitelist (free of charge access)", + "verbose_name_plural": "whitelists (free of charge access)", + }, + ), + migrations.CreateModel( + name="Request", + bases=(models.Model,), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "type", + models.CharField( + max_length=2, + choices=(("PW", "Password"), ("EM", "Email address")), + ), + ), + ("token", models.CharField(max_length=32)), + ("email", models.EmailField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True, editable=False)), + ("expires_at", models.DateTimeField()), + ], + ), + migrations.CreateModel( + name="EMailAddress", + bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "local_part", + models.CharField( + unique=True, + max_length=128, + help_text="Local part of the email address.", + ), + ), + ], + options={ + "permissions": ( + ("view_emailaddress", "Can view a local email account object"), + ), + "verbose_name": "local email account", + "verbose_name_plural": "local email accounts", + }, + ), + ] From d58f90ecee83cb6c42f0777343e2bbf265b2dba4 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 29 Dec 2020 17:42:40 +0100 Subject: [PATCH 453/490] Topologie models creation. --- topologie/migrations/0001_squashed_0074.py | 590 +++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 topologie/migrations/0001_squashed_0074.py diff --git a/topologie/migrations/0001_squashed_0074.py b/topologie/migrations/0001_squashed_0074.py new file mode 100644 index 00000000..32ed6585 --- /dev/null +++ b/topologie/migrations/0001_squashed_0074.py @@ -0,0 +1,590 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +from django.conf import settings +import django.contrib.auth.models +import django.core.validators +import re2o.mixins +import re2o.field_permissions + + +class Migration(migrations.Migration): + initial = True + dependencies = [("machines", "0001_squashed_0108")] + replaces = [ + ("topologie", "0001_initial.py"), + ("topologie", "0002_auto_20160703_1118.py"), + ("topologie", "0003_room.py"), + ("topologie", "0004_auto_20160703_1122.py"), + ("topologie", "0005_auto_20160703_1123.py"), + ("topologie", "0006_auto_20160703_1129.py"), + ("topologie", "0007_auto_20160703_1148.py"), + ("topologie", "0008_port_room.py"), + ("topologie", "0009_auto_20160703_1200.py"), + ("topologie", "0010_auto_20160704_2148.py"), + ("topologie", "0011_auto_20160704_2153.py"), + ("topologie", "0012_port_machine_interface.py"), + ("topologie", "0013_port_related.py"), + ("topologie", "0014_auto_20160706_1238.py"), + ("topologie", "0015_auto_20160706_1452.py"), + ("topologie", "0016_auto_20160706_1531.py"), + ("topologie", "0017_auto_20160718_1141.py"), + ("topologie", "0018_room_details.py"), + ("topologie", "0019_auto_20161026_1348.py"), + ("topologie", "0020_auto_20161119_0033.py"), + ("topologie", "0021_port_radius.py"), + ("topologie", "0022_auto_20161211_1622.py"), + ("topologie", "0023_auto_20170817_1654.py"), + ("topologie", "0023_auto_20170826_1530.py"), + ("topologie", "0024_auto_20170818_1021.py"), + ("topologie", "0024_auto_20170826_1800.py"), + ("topologie", "0025_merge_20170902_1242.py"), + ("topologie", "0026_auto_20170902_1245.py"), + ("topologie", "0027_auto_20170905_1442.py"), + ("topologie", "0028_auto_20170913_1503.py"), + ("topologie", "0029_auto_20171002_0334.py"), + ("topologie", "0030_auto_20171004_0235.py"), + ("topologie", "0031_auto_20171015_2033.py"), + ("topologie", "0032_auto_20171026_0338.py"), + ("topologie", "0033_auto_20171231_1743.py"), + ("topologie", "0034_borne.py"), + ("topologie", "0035_auto_20180324_0023.py"), + ("topologie", "0036_transferborne.py"), + ("topologie", "0037_auto_20180325_0127.py"), + ("topologie", "0038_transfersw.py"), + ("topologie", "0039_port_new_switch.py"), + ("topologie", "0040_transferports.py"), + ("topologie", "0041_transferportsw.py"), + ("topologie", "0042_transferswitch.py"), + ("topologie", "0043_renamenewswitch.py"), + ("topologie", "0044_auto_20180326_0002.py"), + ("topologie", "0045_auto_20180326_0123.py"), + ("topologie", "0046_auto_20180326_0129.py"), + ("topologie", "0047_ap_machine.py"), + ("topologie", "0048_ap_machine.py"), + ("topologie", "0049_switchs_machine.py"), + ("topologie", "0050_port_new_switch.py"), + ("topologie", "0051_switchs_machine.py"), + ("topologie", "0052_transferports.py"), + ("topologie", "0053_finalsw.py"), + ("topologie", "0054_auto_20180326_1742.py"), + ("topologie", "0055_auto_20180329_0431.py"), + ("topologie", "0056_building_switchbay.py"), + ("topologie", "0057_auto_20180408_0316.py"), + ("topologie", "0058_remove_switch_location.py"), + ("topologie", "0059_auto_20180415_2249.py"), + ("topologie", "0060_server.py"), + ("topologie", "0061_portprofile.py"), + ("topologie", "0062_auto_20180815_1918.py"), + ("topologie", "0063_auto_20180919_2225.py"), + ("topologie", "0064_switch_automatic_provision.py"), + ("topologie", "0065_auto_20180927_1836.py"), + ("topologie", "0066_modelswitch_commercial_name.py"), + ("topologie", "0067_auto_20181230_1819.py"), + ("topologie", "0068_auto_20190102_1758.py"), + ("topologie", "0069_auto_20190108_1439.py"), + ("topologie", "0070_auto_20190218_1743.py"), + ("topologie", "0071_auto_20190218_1936.py"), + ("topologie", "0072_auto_20190720_2318.py"), + ("topologie", "0073_auto_20191120_0159.py"), + ("topologie", "0074_auto_20200419_1640.py"), + ] + operations = [ + migrations.CreateModel( + name="Stack", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=32, blank=True, null=True)), + ("stack_id", models.CharField(max_length=32, unique=True)), + ("details", models.CharField(max_length=255, blank=True, null=True)), + ("member_id_min", models.PositiveIntegerField()), + ("member_id_max", models.PositiveIntegerField()), + ], + options={ + "permissions": (("view_stack", "Can view a stack object"),), + "verbose_name": "switches stack", + "verbose_name_plural": "switches stacks", + }, + ), + migrations.CreateModel( + name="AccessPoint", + bases=("machines.machine",), + fields=[ + ( + "machine_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Machine", + ), + ), + ( + "location", + models.CharField( + max_length=255, + help_text="Details about the AP's location.", + blank=True, + null=True, + ), + ), + ], + options={ + "permissions": ( + ("view_accesspoint", "Can view an access point object"), + ), + "verbose_name": "access point", + "verbose_name_plural": "access points", + }, + ), + migrations.CreateModel( + name="Server", + bases=("machines.machine",), + fields=[], + options={"proxy": True}, + ), + migrations.CreateModel( + name="Switch", + bases=("machines.machine",), + fields=[ + ( + "machine_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="machines.Machine", + ), + ), + ("number", models.PositiveIntegerField(help_text="Number of ports.")), + ("stack_member_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "automatic_provision", + models.BooleanField( + default=False, help_text="Automatic provision for the switch." + ), + ), + ], + options={ + "permissions": (("view_switch", "Can view a switch object"),), + "verbose_name": "switch", + "verbose_name_plural": "switches", + }, + ), + migrations.CreateModel( + name="ModelSwitch", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("reference", models.CharField(max_length=255)), + ( + "commercial_name", + models.CharField(max_length=255, null=True, blank=True), + ), + ("firmware", models.CharField(max_length=255, null=True, blank=True)), + ( + "is_modular", + models.BooleanField( + default=False, help_text="The switch model is modular." + ), + ), + ( + "is_itself_module", + models.BooleanField( + default=False, help_text="The switch is considered as a module." + ), + ), + ], + options={ + "permissions": ( + ("view_modelswitch", "Can view a switch model object"), + ), + "verbose_name": "switch model", + "verbose_name_plural": "switch models", + }, + ), + migrations.CreateModel( + name="ModuleSwitch", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "reference", + models.CharField( + max_length=255, + help_text="Reference of a module.", + verbose_name="module reference", + ), + ), + ( + "comment", + models.CharField( + max_length=255, + null=True, + blank=True, + help_text="Comment.", + verbose_name="comment", + ), + ), + ], + options={ + "permissions": ( + ("view_moduleswitch", "Can view a switch module object"), + ), + "verbose_name": "switch module", + "verbose_name_plural": "switch modules", + }, + ), + migrations.CreateModel( + name="ModuleOnSwitch", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "slot", + models.CharField( + max_length=15, help_text="Slot on switch.", verbose_name="slot" + ), + ), + ], + options={ + "permissions": ( + ( + "view_moduleonswitch", + "Can view a link between switch and module object", + ), + ), + "verbose_name": "link between switch and module", + "verbose_name_plural": "links between switch and module", + }, + ), + migrations.CreateModel( + name="ConstructorSwitch", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": ( + ("view_constructorswitch", "Can view a switch constructor object"), + ), + "verbose_name": "switch constructor", + "verbose_name_plural": "switch constructors", + }, + ), + migrations.CreateModel( + name="SwitchBay", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("info", models.CharField(max_length=255, blank=True, null=True)), + ], + options={ + "permissions": (("view_switchbay", "Can view a switch bay object"),), + "verbose_name": "switch bay", + "verbose_name_plural": "switch bays", + }, + ), + migrations.CreateModel( + name="Dormitory", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": (("view_dormitory", "Can view a dormitory object"),), + "verbose_name": "dormitory", + "verbose_name_plural": "dormitories", + }, + ), + migrations.CreateModel( + name="Building", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": (("view_building", "Can view a building object"),), + "verbose_name": "building", + "verbose_name_plural": "buildings", + }, + ), + migrations.CreateModel( + name="Port", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("port", models.PositiveIntegerField()), + ( + "state", + models.BooleanField( + default=True, + help_text="Port state Active.", + verbose_name="port state Active", + ), + ), + ("details", models.CharField(max_length=255, blank=True)), + ], + options={ + "permissions": (("view_port", "Can view a port object"),), + "verbose_name": "port", + "verbose_name_plural": "port", + }, + ), + migrations.CreateModel( + name="PortProfile", + bases=( + re2o.mixins.AclMixin, + re2o.mixins.RevMixin, + models.Model, + ), + fields=[ + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255, verbose_name="name")), + ( + "profil_default", + models.CharField( + max_length=32, + choices=( + ("room", "Room"), + ("access_point", "Access point"), + ("uplink", "Uplink"), + ("asso_machine", "Organisation machine"), + ("nothing", "Nothing"), + ), + blank=True, + null=True, + verbose_name="default profile", + ), + ), + ( + "radius_type", + models.CharField( + max_length=32, + choices=( + ("NO", "NO"), + ("802.1X", "802.1X"), + ("MAC-radius", "MAC-RADIUS"), + ), + help_text="Type of RADIUS authentication: inactive, MAC-address or 802.1X.", + verbose_name="RADIUS type", + ), + ), + ( + "radius_mode", + models.CharField( + max_length=32, + choices=(("STRICT", "STRICT"), ("COMMON", "COMMON")), + default="COMMON", + help_text="In case of MAC-authentication: mode COMMON or STRICT on this port.", + verbose_name="RADIUS mode", + ), + ), + ( + "speed", + models.CharField( + max_length=32, + 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.", + ), + ), + ( + "mac_limit", + models.IntegerField( + null=True, + blank=True, + help_text="Limit of MAC-address on this port.", + verbose_name="MAC limit", + ), + ), + ( + "flow_control", + models.BooleanField(default=False, help_text="Flow control."), + ), + ( + "dhcp_snooping", + models.BooleanField( + default=False, + help_text="Protect against rogue DHCP.", + verbose_name="DHCP snooping", + ), + ), + ( + "dhcpv6_snooping", + models.BooleanField( + default=False, + help_text="Protect against rogue DHCPv6.", + verbose_name="DHCPv6 snooping", + ), + ), + ( + "arp_protect", + models.BooleanField( + efault=False, + help_text="Check if IP address is DHCP assigned.", + verbose_name="ARP protection", + ), + ), + ( + "ra_guard", + models.BooleanField( + default=False, + help_text="Protect against rogue RA.", + verbose_name="RA guard", + ), + ), + ( + "loop_protect", + models.BooleanField( + default=False, + help_text="Protect against loop.", + verbose_name="loop protection", + ), + ), + ], + options={ + "permissions": ( + ("view_portprofile", "Can view a port profile object"), + ), + "verbose_name": "port profile", + "verbose_name_plural": "port profiles", + }, + ), + ] From 77f5b6632943a7bdd7f44a336cb3c588aea0f00c Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 13:52:41 +0100 Subject: [PATCH 454/490] fix dependencies names --- api/acl.py | 2 +- cotisations/migrations/0001_squashed_0050.py | 331 ++++++++++++------- machines/migrations/0001_squashed_0108.py | 218 ++++++------ preferences/migrations/0001_squashed_0071.py | 171 +++++----- topologie/migrations/0001_squashed_0074.py | 154 ++++----- users/migrations/0001_squashed_0095.py | 195 ++++++----- 6 files changed, 583 insertions(+), 488 deletions(-) diff --git a/api/acl.py b/api/acl.py index 829f8ea9..c745158f 100644 --- a/api/acl.py +++ b/api/acl.py @@ -53,7 +53,7 @@ def _create_api_permission(): api_permission.save() -_create_api_permission() +#_create_api_permission() def can_view(user, *args, **kwargs): diff --git a/cotisations/migrations/0001_squashed_0050.py b/cotisations/migrations/0001_squashed_0050.py index ebc02daa..4df7dc93 100644 --- a/cotisations/migrations/0001_squashed_0050.py +++ b/cotisations/migrations/0001_squashed_0050.py @@ -15,57 +15,57 @@ class Migration(migrations.Migration): initial = True dependencies = [] replaces = [ - ("cotisations", "0001_initial.py"), - ("cotisations", "0002_remove_facture_article.py"), - ("cotisations", "0003_auto_20160702_1448.py"), - ("cotisations", "0004_auto_20160702_1528.py"), - ("cotisations", "0005_auto_20160702_1532.py"), - ("cotisations", "0006_auto_20160702_1534.py"), - ("cotisations", "0007_auto_20160702_1543.py"), - ("cotisations", "0008_auto_20160702_1614.py"), - ("cotisations", "0009_remove_cotisation_user.py"), - ("cotisations", "0010_auto_20160702_1840.py"), - ("cotisations", "0011_auto_20160702_1911.py"), - ("cotisations", "0012_auto_20160704_0118.py"), - ("cotisations", "0013_auto_20160711_2240.py"), - ("cotisations", "0014_auto_20160712_0245.py"), - ("cotisations", "0015_auto_20160714_2142.py"), - ("cotisations", "0016_auto_20160715_0110.py"), - ("cotisations", "0017_auto_20170718_2329.py"), - ("cotisations", "0018_paiement_type_paiement.py"), - ("cotisations", "0019_auto_20170819_0055.py"), - ("cotisations", "0020_auto_20170819_0057.py"), - ("cotisations", "0021_auto_20170819_0104.py"), - ("cotisations", "0022_auto_20170824_0128.py"), - ("cotisations", "0023_auto_20170902_1303.py"), - ("cotisations", "0024_auto_20171015_2033.py"), - ("cotisations", "0025_article_type_user.py"), - ("cotisations", "0026_auto_20171028_0126.py"), - ("cotisations", "0027_auto_20171029_1156.py"), - ("cotisations", "0028_auto_20171231_0007.py"), - ("cotisations", "0029_auto_20180414_2056.py"), - ("cotisations", "0030_custom_payment.py"), - ("cotisations", "0031_comnpaypayment_production.py"), - ("cotisations", "0032_custom_invoice.py"), - ("cotisations", "0033_auto_20180818_1319.py"), - ("cotisations", "0034_auto_20180831_1532.py"), - ("cotisations", "0035_notepayment.py"), - ("cotisations", "0036_custominvoice_remark.py"), - ("cotisations", "0037_costestimate.py"), - ("cotisations", "0038_auto_20181231_1657.py"), - ("cotisations", "0039_freepayment.py"), - ("cotisations", "0040_auto_20191002_2335.py"), - ("cotisations", "0041_auto_20191103_2131.py"), - ("cotisations", "0042_auto_20191120_0159.py"), - ("cotisations", "0043_separation_membership_connection_p1.py"), - ("cotisations", "0044_separation_membership_connection_p2.py"), - ("cotisations", "0045_separation_membership_connection_p3.py"), - ("cotisations", "0046_article_need_membership.py"), - ("cotisations", "0047_article_need_membership_init.py"), - ("cotisations", "0048_auto_20201017_0018.py"), - ("cotisations", "0049_auto_20201102_2305.py"), - ("cotisations", "0050_auto_20201102_2342.py"), - ("cotisations", "0051_auto_20201228_1636.py"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), ] operations = [ migrations.CreateModel( @@ -317,24 +317,34 @@ class Migration(migrations.Migration): default=True, verbose_name="need membership to be purchased" ), ), - ("type_user", models.CharField( - choices=[ - ("Adherent", "Member"), - ("Club", "Club"), - ("All", "Both of them"), - ], - default="All", - max_length=255, - verbose_name="type of users concerned", - )), - ("available_for_everyone", models.BooleanField(default=False, verbose_name="is available for every user")), - + ( + "type_user", + models.CharField( + choices=[ + ("Adherent", "Member"), + ("Club", "Club"), + ("All", "Both of them"), + ], + default="All", + max_length=255, + verbose_name="type of users concerned", + ), + ), + ( + "available_for_everyone", + models.BooleanField( + default=False, verbose_name="is available for every user" + ), + ), ], options={ - "permissions":(("view_article", "Can view an article object"),("buy_every_article", "Can buy every article")), - "verbose_name":"article", - "verbose_name_plural":"articles" - } + "permissions": ( + ("view_article", "Can view an article object"), + ("buy_every_article", "Can buy every article"), + ), + "verbose_name": "article", + "verbose_name_plural": "articles", + }, ), migrations.CreateModel( name="Banque", @@ -353,10 +363,14 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("name", models.CharField(max_length=255)) - ], - options={"permissions": (("view_banque", "Can view a bank object"),), "verbose_name":"bank", "verbose_name_plural":"banks"} - ), + ("name", models.CharField(max_length=255)), + ], + options={ + "permissions": (("view_banque", "Can view a bank object"),), + "verbose_name": "bank", + "verbose_name_plural": "banks", + }, + ), migrations.CreateModel( name="Paiement", bases=( @@ -375,15 +389,33 @@ class Migration(migrations.Migration): ), ), ("moyen", models.CharField(max_length=255, verbose_name="method")), - ("available_for_everyone", models.BooleanField(default=False, verbose_name="is available for every user",)), - ("is_balance", models.BooleanField(default=False,editable=False, verbose_name="is user balance",help_text="There should be only one balance payment method.",validators=[cotisations.models.check_no_balance])) + ( + "available_for_everyone", + models.BooleanField( + default=False, + verbose_name="is available for every user", + ), + ), + ( + "is_balance", + models.BooleanField( + default=False, + editable=False, + verbose_name="is user balance", + help_text="There should be only one balance payment method.", + validators=[cotisations.models.check_no_balance], + ), + ), ], options={ - "permissions":(("view_paiement", "Can view a payment method object"), ("use_every_payment", "Can use every payment method")), + "permissions": ( + ("view_paiement", "Can view a payment method object"), + ("use_every_payment", "Can use every payment method"), + ), "verbose_name": "payment method", - "verbose_name_plural": "payment methods" - } - ), + "verbose_name_plural": "payment methods", + }, + ), migrations.CreateModel( name="Cotisation", bases=( @@ -401,18 +433,32 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("date_start_con", models.DateTimeField(verbose_name="start date for the connection")), - ("date_end_con", models.DateTimeField(verbose_name="end date for the connection")), - ("date_start_memb", models.DateTimeField(verbose_name="start date for the membership")), - ("date_end_memb", models.DateTimeField(verbose_name="end date for the membership")) - - ], + ( + "date_start_con", + models.DateTimeField(verbose_name="start date for the connection"), + ), + ( + "date_end_con", + models.DateTimeField(verbose_name="end date for the connection"), + ), + ( + "date_start_memb", + models.DateTimeField(verbose_name="start date for the membership"), + ), + ( + "date_end_memb", + models.DateTimeField(verbose_name="end date for the membership"), + ), + ], options={ - "permissions":( ("view_cotisation", "Can view a subscription object"),("change_all_cotisation", "Can edit the previous subscriptions")), - "verbose_name":"subscription", - "verbose_name_plural":"subscriptions" - } - ), + "permissions": ( + ("view_cotisation", "Can view a subscription object"), + ("change_all_cotisation", "Can edit the previous subscriptions"), + ), + "verbose_name": "subscription", + "verbose_name_plural": "subscriptions", + }, + ), migrations.CreateModel( name="BalancePayment", bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), @@ -426,13 +472,37 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("minimum_balance", models.DecimalField(verbose_name="minimum balance",help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.", max_digits=5, decimal_places=2, default=0)), - ("maximum_balance", models.DecimalField(verbose_name="maximum balance", help_text="The maximal amount of money allowed for the balance.", max_digits=5, decimal_places=2, default=50, blank=True, null=True)), - ("credit_balance_allowed", models.BooleanField(verbose_name="allow user to credit their balance", default=False)) - - ], - options={"verbose_name", "user balance"} - ), + ( + "minimum_balance", + models.DecimalField( + verbose_name="minimum balance", + help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.", + max_digits=5, + decimal_places=2, + default=0, + ), + ), + ( + "maximum_balance", + models.DecimalField( + verbose_name="maximum balance", + help_text="The maximal amount of money allowed for the balance.", + max_digits=5, + decimal_places=2, + default=50, + blank=True, + null=True, + ), + ), + ( + "credit_balance_allowed", + models.BooleanField( + verbose_name="allow user to credit their balance", default=False + ), + ), + ], + options={"verbose_name", "user balance"}, + ), migrations.CreateModel( name="ChequePayment", bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), @@ -446,9 +516,9 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ], - options={"verbose_name", "cheque"} - ), + ], + options={"verbose_name", "cheque"}, + ), migrations.CreateModel( name="ComnpayPayment", bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), @@ -462,13 +532,44 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("payment_credential", models.CharField(max_length=255, default="", blank=True, verbose_name="ComNpay VAT Number")), - ("payment_pass", re2o.aes_field.AESEncryptedField(max_length=255, null=True, blank=True, verbose_name="ComNpay secret key")), - ("minimum_payment", models.DecimalField(verbose_name="minimum payment", help_text="The minimal amount of money you have to use when paying with ComNpay.", max_digits=5,decimal_places=2,default=1)), - ("production", models.BooleanField(default=True, verbose_name="production mode enabled (production URL, instead of homologation)")) - ], - options={"verbose_name", "ComNpay"} - ), + ( + "payment_credential", + models.CharField( + max_length=255, + default="", + blank=True, + verbose_name="ComNpay VAT Number", + ), + ), + ( + "payment_pass", + re2o.aes_field.AESEncryptedField( + max_length=255, + null=True, + blank=True, + verbose_name="ComNpay secret key", + ), + ), + ( + "minimum_payment", + models.DecimalField( + verbose_name="minimum payment", + help_text="The minimal amount of money you have to use when paying with ComNpay.", + max_digits=5, + decimal_places=2, + default=1, + ), + ), + ( + "production", + models.BooleanField( + default=True, + verbose_name="production mode enabled (production URL, instead of homologation)", + ), + ), + ], + options={"verbose_name", "ComNpay"}, + ), migrations.CreateModel( name="FreePayment", bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), @@ -482,9 +583,9 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ], - options={"verbose_name", "Free payment"} - ), + ], + options={"verbose_name", "Free payment"}, + ), migrations.CreateModel( name="NotePayment", bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model), @@ -500,12 +601,8 @@ class Migration(migrations.Migration): ), ("server", models.CharField(max_length=255, verbose_name="server")), ("port", models.PositiveIntegerField(blank=True, null=True)), - ("id_note", models.PositiveIntegerField(blank=True, null=True)) - ], - options={"verbose_name", "NoteKfet"} - ), - ] - - - - + ("id_note", models.PositiveIntegerField(blank=True, null=True)), + ], + options={"verbose_name", "NoteKfet"}, + ), + ] diff --git a/machines/migrations/0001_squashed_0108.py b/machines/migrations/0001_squashed_0108.py index db54d296..3341abf8 100644 --- a/machines/migrations/0001_squashed_0108.py +++ b/machines/migrations/0001_squashed_0108.py @@ -14,115 +14,115 @@ class Migration(migrations.Migration): initial = True dependencies = [] replaces = [ - ("machines", "0001_initial.py"), - ("machines", "0001_squashed_0108.py"), - ("machines", "0002_auto_20160703_1444.py"), - ("machines", "0003_auto_20160703_1450.py"), - ("machines", "0004_auto_20160703_1451.py"), - ("machines", "0005_auto_20160703_1523.py"), - ("machines", "0006_auto_20160703_1813.py"), - ("machines", "0007_auto_20160703_1816.py"), - ("machines", "0008_remove_interface_ipv6.py"), - ("machines", "0009_auto_20160703_2358.py"), - ("machines", "0010_auto_20160704_0104.py"), - ("machines", "0011_auto_20160704_0105.py"), - ("machines", "0012_auto_20160704_0118.py"), - ("machines", "0013_auto_20160705_1014.py"), - ("machines", "0014_auto_20160706_1220.py"), - ("machines", "0015_auto_20160707_0105.py"), - ("machines", "0016_auto_20160708_1633.py"), - ("machines", "0017_auto_20160708_1645.py"), - ("machines", "0018_auto_20160708_1813.py"), - ("machines", "0019_auto_20160718_1141.py"), - ("machines", "0020_auto_20160718_1849.py"), - ("machines", "0021_auto_20161006_1943.py"), - ("machines", "0022_auto_20161011_1829.py"), - ("machines", "0023_iplist_ip_type.py"), - ("machines", "0024_machinetype_need_infra.py"), - ("machines", "0025_auto_20161023_0038.py"), - ("machines", "0026_auto_20161026_1348.py"), - ("machines", "0027_alias.py"), - ("machines", "0028_iptype_domaine_ip.py"), - ("machines", "0029_iptype_domaine_range.py"), - ("machines", "0030_auto_20161118_1730.py"), - ("machines", "0031_auto_20161119_1709.py"), - ("machines", "0032_auto_20161119_1850.py"), - ("machines", "0033_extension_need_infra.py"), - ("machines", "0034_iplist_need_infra.py"), - ("machines", "0035_auto_20161224_1201.py"), - ("machines", "0036_auto_20161224_1204.py"), - ("machines", "0037_domain_cname.py"), - ("machines", "0038_auto_20161224_1721.py"), - ("machines", "0039_auto_20161224_1732.py"), - ("machines", "0040_remove_interface_dns.py"), - ("machines", "0041_remove_ns_interface.py"), - ("machines", "0042_ns_ns.py"), - ("machines", "0043_auto_20170721_0350.py"), - ("machines", "0044_auto_20170808_0233.py"), - ("machines", "0045_auto_20170808_0348.py"), - ("machines", "0046_auto_20170808_1423.py"), - ("machines", "0047_auto_20170809_0606.py"), - ("machines", "0048_auto_20170823_2315.py"), - ("machines", "0049_vlan.py"), - ("machines", "0050_auto_20170826_0022.py"), - ("machines", "0051_iptype_vlan.py"), - ("machines", "0052_auto_20170828_2322.py"), - ("machines", "0053_text.py"), - ("machines", "0054_text_zone.py"), - ("machines", "0055_nas.py"), - ("machines", "0056_nas_port_access_mode.py"), - ("machines", "0057_nas_autocapture_mac.py"), - ("machines", "0058_auto_20171002_0350.py"), - ("machines", "0059_iptype_prefix_v6.py"), - ("machines", "0060_iptype_ouverture_ports.py"), - ("machines", "0061_auto_20171015_2033.py"), - ("machines", "0062_extension_origin_v6.py"), - ("machines", "0063_auto_20171020_0040.py"), - ("machines", "0064_auto_20171115_0253.py"), - ("machines", "0065_auto_20171115_1514.py"), - ("machines", "0066_srv.py"), - ("machines", "0067_auto_20171116_0152.py"), - ("machines", "0068_auto_20171116_0252.py"), - ("machines", "0069_auto_20171116_0822.py"), - ("machines", "0070_auto_20171231_1947.py"), - ("machines", "0071_auto_20171231_2100.py"), - ("machines", "0072_auto_20180108_1822.py"), - ("machines", "0073_auto_20180128_2203.py"), - ("machines", "0074_auto_20180129_0352.py"), - ("machines", "0075_auto_20180130_0052.py"), - ("machines", "0076_auto_20180130_1623.py"), - ("machines", "0077_auto_20180409_2243.py"), - ("machines", "0078_auto_20180415_1252.py"), - ("machines", "0079_auto_20180416_0107.py"), - ("machines", "0080_auto_20180502_2334.py"), - ("machines", "0081_auto_20180521_1413.py"), - ("machines", "0082_auto_20180525_2209.py"), - ("machines", "0083_remove_duplicate_rights.py"), - ("machines", "0084_dname.py"), - ("machines", "0085_sshfingerprint.py"), - ("machines", "0086_role.py"), - ("machines", "0087_dnssec.py"), - ("machines", "0088_iptype_prefix_v6_length.py"), - ("machines", "0089_auto_20180805_1148.py"), - ("machines", "0090_auto_20180805_1459.py"), - ("machines", "0091_auto_20180806_2310.py"), - ("machines", "0092_auto_20180807_0926.py"), - ("machines", "0093_auto_20180807_1115.py"), - ("machines", "0094_auto_20180815_1918.py"), - ("machines", "0095_auto_20180919_2225.py"), - ("machines", "0096_auto_20181013_1417.py"), - ("machines", "0097_extension_dnssec.py"), - ("machines", "0098_auto_20190102_1745.py"), - ("machines", "0099_role_recursive_dns.py"), - ("machines", "0100_auto_20190102_1753.py"), - ("machines", "0101_auto_20190108_1623.py"), - ("machines", "0102_auto_20190303_1611.py"), - ("machines", "0103_auto_20191002_2222.py"), - ("machines", "0104_auto_20191002_2231.py"), - ("machines", "0105_dname_ttl.py"), - ("machines", "0106_auto_20191120_0159.py"), - ("machines", "0107_fix_lowercase_domain.py"), - ("machines", "0108_ipv6list_active.py"), + ("machines", "0001_initial"), + ("machines", "0001_squashed_0108"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), ] operations = [ migrations.CreateModel( diff --git a/preferences/migrations/0001_squashed_0071.py b/preferences/migrations/0001_squashed_0071.py index 2d8c620c..4137aaea 100644 --- a/preferences/migrations/0001_squashed_0071.py +++ b/preferences/migrations/0001_squashed_0071.py @@ -7,94 +7,93 @@ import re2o.aes_field class Migration(migrations.Migration): - initial = True dependencies = [] replaces = [ - ("preferences", "0001_initial.py"), - ("preferences", "0001_squashed_0071.py"), - ("preferences", "0002_auto_20170625_1923.py"), - ("preferences", "0003_optionaluser_solde_negatif.py"), - ("preferences", "0004_assooption_services.py"), - ("preferences", "0005_auto_20170824_0139.py"), - ("preferences", "0006_auto_20170824_0143.py"), - ("preferences", "0007_auto_20170824_2056.py"), - ("preferences", "0008_auto_20170824_2122.py"), - ("preferences", "0009_assooption_utilisateur_asso.py"), - ("preferences", "0010_auto_20170825_0459.py"), - ("preferences", "0011_auto_20170825_2307.py"), - ("preferences", "0012_generaloption_req_expire_hrs.py"), - ("preferences", "0013_generaloption_site_name.py"), - ("preferences", "0014_generaloption_email_from.py"), - ("preferences", "0015_optionaltopologie_radius_general_policy.py"), - ("preferences", "0016_auto_20170902_1520.py"), - ("preferences", "0017_mailmessageoption.py"), - ("preferences", "0018_optionaltopologie_mac_autocapture.py"), - ("preferences", "0019_remove_optionaltopologie_mac_autocapture.py"), - ("preferences", "0020_optionalmachine_ipv6.py"), - ("preferences", "0021_auto_20171015_1741.py"), - ("preferences", "0022_auto_20171015_1758.py"), - ("preferences", "0023_auto_20171015_2033.py"), - ("preferences", "0024_optionaluser_all_can_create.py"), - ("preferences", "0025_auto_20171231_2142.py"), - ("preferences", "0025_generaloption_general_message.py"), - ("preferences", "0026_auto_20171216_0401.py"), - ("preferences", "0027_merge_20180106_2019.py"), - ("preferences", "0028_assooption_description.py"), - ("preferences", "0028_auto_20180111_1129.py"), - ("preferences", "0028_auto_20180128_2203.py"), - ("preferences", "0029_auto_20180111_1134.py"), - ("preferences", "0029_auto_20180318_0213.py"), - ("preferences", "0029_auto_20180318_1005.py"), - ("preferences", "0030_auto_20180111_2346.py"), - ("preferences", "0030_merge_20180320_1419.py"), - ("preferences", "0031_auto_20180323_0218.py"), - ("preferences", "0031_optionaluser_self_adhesion.py"), - ("preferences", "0032_optionaluser_min_online_payment.py"), - ("preferences", "0032_optionaluser_shell_default.py"), - ("preferences", "0033_accueiloption.py"), - ("preferences", "0033_generaloption_gtu_sum_up.py"), - ("preferences", "0034_auto_20180114_2025.py"), - ("preferences", "0034_auto_20180416_1120.py"), - ("preferences", "0035_auto_20180114_2132.py"), - ("preferences", "0035_optionaluser_allow_self_subscription.py"), - ("preferences", "0036_auto_20180114_2141.py"), - ("preferences", "0037_auto_20180114_2156.py"), - ("preferences", "0038_auto_20180114_2209.py"), - ("preferences", "0039_auto_20180115_0003.py"), - ("preferences", "0040_auto_20180129_1745.py"), - ("preferences", "0041_merge_20180130_0052.py"), - ("preferences", "0042_auto_20180222_1743.py"), - ("preferences", "0043_optionalmachine_create_machine.py"), - ("preferences", "0044_remove_payment_pass.py"), - ("preferences", "0045_remove_unused_payment_fields.py"), - ("preferences", "0046_optionaluser_mail_extension.py"), - ("preferences", "0047_mailcontact.py"), - ("preferences", "0048_auto_20180811_1515.py"), - ("preferences", "0049_optionaluser_self_change_shell.py"), - ("preferences", "0050_auto_20180818_1329.py"), - ("preferences", "0051_auto_20180919_2225.py"), - ("preferences", "0052_optionaluser_delete_notyetactive.py"), - ("preferences", "0053_optionaluser_self_change_room.py"), - ("preferences", "0055_generaloption_main_site_url.py"), - ("preferences", "0056_1_radiusoption.py"), - ("preferences", "0056_2_radiusoption.py"), - ("preferences", "0056_3_radiusoption.py"), - ("preferences", "0056_4_radiusoption.py"), - ("preferences", "0057_optionaluser_all_users_active.py"), - ("preferences", "0058_auto_20190108_1650.py"), - ("preferences", "0059_auto_20190120_1739.py"), - ("preferences", "0060_auto_20190712_1821.py"), - ("preferences", "0061_optionaluser_allow_archived_connexion.py"), - ("preferences", "0062_auto_20190910_1909.py"), - ("preferences", "0063_mandate.py"), - ("preferences", "0064_auto_20191008_1335.py"), - ("preferences", "0065_auto_20191010_1227.py"), - ("preferences", "0066_optionalmachine_default_dns_ttl.py"), - ("preferences", "0067_auto_20191120_0159.py"), - ("preferences", "0068_optionaluser_allow_set_password_during_user_creation.py"), - ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed.py"), - ("preferences", "0070_auto_20200419_0225.py"), - ("preferences", "0071_optionaluser_self_change_pseudo.py"), + ("preferences", "0001_initial"), + ("preferences", "0001_squashed_0071"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), ] operations = [ migrations.CreateModel( diff --git a/topologie/migrations/0001_squashed_0074.py b/topologie/migrations/0001_squashed_0074.py index 32ed6585..6ed9ca8b 100644 --- a/topologie/migrations/0001_squashed_0074.py +++ b/topologie/migrations/0001_squashed_0074.py @@ -14,82 +14,82 @@ class Migration(migrations.Migration): initial = True dependencies = [("machines", "0001_squashed_0108")] replaces = [ - ("topologie", "0001_initial.py"), - ("topologie", "0002_auto_20160703_1118.py"), - ("topologie", "0003_room.py"), - ("topologie", "0004_auto_20160703_1122.py"), - ("topologie", "0005_auto_20160703_1123.py"), - ("topologie", "0006_auto_20160703_1129.py"), - ("topologie", "0007_auto_20160703_1148.py"), - ("topologie", "0008_port_room.py"), - ("topologie", "0009_auto_20160703_1200.py"), - ("topologie", "0010_auto_20160704_2148.py"), - ("topologie", "0011_auto_20160704_2153.py"), - ("topologie", "0012_port_machine_interface.py"), - ("topologie", "0013_port_related.py"), - ("topologie", "0014_auto_20160706_1238.py"), - ("topologie", "0015_auto_20160706_1452.py"), - ("topologie", "0016_auto_20160706_1531.py"), - ("topologie", "0017_auto_20160718_1141.py"), - ("topologie", "0018_room_details.py"), - ("topologie", "0019_auto_20161026_1348.py"), - ("topologie", "0020_auto_20161119_0033.py"), - ("topologie", "0021_port_radius.py"), - ("topologie", "0022_auto_20161211_1622.py"), - ("topologie", "0023_auto_20170817_1654.py"), - ("topologie", "0023_auto_20170826_1530.py"), - ("topologie", "0024_auto_20170818_1021.py"), - ("topologie", "0024_auto_20170826_1800.py"), - ("topologie", "0025_merge_20170902_1242.py"), - ("topologie", "0026_auto_20170902_1245.py"), - ("topologie", "0027_auto_20170905_1442.py"), - ("topologie", "0028_auto_20170913_1503.py"), - ("topologie", "0029_auto_20171002_0334.py"), - ("topologie", "0030_auto_20171004_0235.py"), - ("topologie", "0031_auto_20171015_2033.py"), - ("topologie", "0032_auto_20171026_0338.py"), - ("topologie", "0033_auto_20171231_1743.py"), - ("topologie", "0034_borne.py"), - ("topologie", "0035_auto_20180324_0023.py"), - ("topologie", "0036_transferborne.py"), - ("topologie", "0037_auto_20180325_0127.py"), - ("topologie", "0038_transfersw.py"), - ("topologie", "0039_port_new_switch.py"), - ("topologie", "0040_transferports.py"), - ("topologie", "0041_transferportsw.py"), - ("topologie", "0042_transferswitch.py"), - ("topologie", "0043_renamenewswitch.py"), - ("topologie", "0044_auto_20180326_0002.py"), - ("topologie", "0045_auto_20180326_0123.py"), - ("topologie", "0046_auto_20180326_0129.py"), - ("topologie", "0047_ap_machine.py"), - ("topologie", "0048_ap_machine.py"), - ("topologie", "0049_switchs_machine.py"), - ("topologie", "0050_port_new_switch.py"), - ("topologie", "0051_switchs_machine.py"), - ("topologie", "0052_transferports.py"), - ("topologie", "0053_finalsw.py"), - ("topologie", "0054_auto_20180326_1742.py"), - ("topologie", "0055_auto_20180329_0431.py"), - ("topologie", "0056_building_switchbay.py"), - ("topologie", "0057_auto_20180408_0316.py"), - ("topologie", "0058_remove_switch_location.py"), - ("topologie", "0059_auto_20180415_2249.py"), - ("topologie", "0060_server.py"), - ("topologie", "0061_portprofile.py"), - ("topologie", "0062_auto_20180815_1918.py"), - ("topologie", "0063_auto_20180919_2225.py"), - ("topologie", "0064_switch_automatic_provision.py"), - ("topologie", "0065_auto_20180927_1836.py"), - ("topologie", "0066_modelswitch_commercial_name.py"), - ("topologie", "0067_auto_20181230_1819.py"), - ("topologie", "0068_auto_20190102_1758.py"), - ("topologie", "0069_auto_20190108_1439.py"), - ("topologie", "0070_auto_20190218_1743.py"), - ("topologie", "0071_auto_20190218_1936.py"), - ("topologie", "0072_auto_20190720_2318.py"), - ("topologie", "0073_auto_20191120_0159.py"), - ("topologie", "0074_auto_20200419_1640.py"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), ] operations = [ migrations.CreateModel( @@ -557,7 +557,7 @@ class Migration(migrations.Migration): ( "arp_protect", models.BooleanField( - efault=False, + default=False, help_text="Check if IP address is DHCP assigned.", verbose_name="ARP protection", ), diff --git a/users/migrations/0001_squashed_0095.py b/users/migrations/0001_squashed_0095.py index 4d426438..e9861e5d 100644 --- a/users/migrations/0001_squashed_0095.py +++ b/users/migrations/0001_squashed_0095.py @@ -12,106 +12,105 @@ import users.models class Migration(migrations.Migration): - initial = True dependencies = [] replaces = [ - ("users", "0001_initial.py"), - ("users", "0002_auto_20160630_2301.py"), - ("users", "0003_listrights_rights.py"), - ("users", "0004_auto_20160701_2312.py"), - ("users", "0005_auto_20160702_0006.py"), - ("users", "0006_ban.py"), - ("users", "0007_auto_20160702_2322.py"), - ("users", "0008_user_registered.py"), - ("users", "0009_user_room.py"), - ("users", "0010_auto_20160703_1226.py"), - ("users", "0011_auto_20160703_1227.py"), - ("users", "0012_auto_20160703_1230.py"), - ("users", "0013_auto_20160704_1547.py"), - ("users", "0014_auto_20160704_1548.py"), - ("users", "0015_whitelist.py"), - ("users", "0016_auto_20160706_1220.py"), - ("users", "0017_auto_20160707_0105.py"), - ("users", "0018_auto_20160707_0115.py"), - ("users", "0019_auto_20160708_1633.py"), - ("users", "0020_request.py"), - ("users", "0021_ldapuser.py"), - ("users", "0022_ldapuser_sambasid.py"), - ("users", "0023_auto_20160724_1908.py"), - ("users", "0024_remove_ldapuser_mac_list.py"), - ("users", "0025_listshell.py"), - ("users", "0026_user_shell.py"), - ("users", "0027_auto_20160726_0216.py"), - ("users", "0028_auto_20160726_0227.py"), - ("users", "0029_auto_20160726_0229.py"), - ("users", "0030_auto_20160726_0357.py"), - ("users", "0031_auto_20160726_0359.py"), - ("users", "0032_auto_20160727_2122.py"), - ("users", "0033_remove_ldapuser_loginshell.py"), - ("users", "0034_auto_20161018_0037.py"), - ("users", "0035_auto_20161018_0046.py"), - ("users", "0036_auto_20161022_2146.py"), - ("users", "0037_auto_20161028_1906.py"), - ("users", "0038_auto_20161031_0258.py"), - ("users", "0039_auto_20161119_0033.py"), - ("users", "0040_auto_20161119_1709.py"), - ("users", "0041_listright_details.py"), - ("users", "0042_auto_20161126_2028.py"), - ("users", "0043_auto_20161224_1156.py"), - ("users", "0043_ban_state.py"), - ("users", "0044_user_ssh_public_key.py"), - ("users", "0045_merge.py"), - ("users", "0046_auto_20170617_1433.py"), - ("users", "0047_auto_20170618_0156.py"), - ("users", "0048_auto_20170618_0210.py"), - ("users", "0049_auto_20170618_1424.py"), - ("users", "0050_serviceuser_comment.py"), - ("users", "0051_user_telephone.py"), - ("users", "0052_ldapuser_shadowexpire.py"), - ("users", "0053_auto_20170626_2105.py"), - ("users", "0054_auto_20170626_2219.py"), - ("users", "0055_auto_20171003_0556.py"), - ("users", "0056_auto_20171015_2033.py"), - ("users", "0057_auto_20171023_0301.py"), - ("users", "0058_auto_20171025_0154.py"), - ("users", "0059_auto_20171025_1854.py"), - ("users", "0060_auto_20171120_0317.py"), - ("users", "0061_auto_20171230_2033.py"), - ("users", "0062_auto_20171231_0056.py"), - ("users", "0063_auto_20171231_0140.py"), - ("users", "0064_auto_20171231_0150.py"), - ("users", "0065_auto_20171231_2053.py"), - ("users", "0066_grouppermissions.py"), - ("users", "0067_serveurpermission.py"), - ("users", "0068_auto_20180107_2245.py"), - ("users", "0069_club_mailing.py"), - ("users", "0070_auto_20180324_1906.py"), - ("users", "0071_auto_20180415_1252.py"), - ("users", "0072_auto_20180426_2021.py"), - ("users", "0073_auto_20180629_1614.py"), - ("users", "0074_auto_20180810_2104.py"), - ("users", "0074_auto_20180814_1059.py"), - ("users", "0075_merge_20180815_2202.py"), - ("users", "0076_auto_20180818_1321.py"), - ("users", "0077_auto_20180824_1750.py"), - ("users", "0078_auto_20181011_1405.py"), - ("users", "0079_auto_20181228_2039.py"), - ("users", "0080_auto_20190108_1726.py"), - ("users", "0081_auto_20190317_0302.py"), - ("users", "0082_auto_20190908_1338.py"), - ("users", "0083_user_shortcuts_enabled.py"), - ("users", "0084_auto_20191120_0159.py"), - ("users", "0085_user_email_state.py"), - ("users", "0086_user_email_change_date.py"), - ("users", "0087_request_email.py"), - ("users", "0088_auto_20200417_2312.py"), - ("users", "0089_auto_20200418_0112.py"), - ("users", "0090_auto_20200421_1825.py"), - ("users", "0091_auto_20200423_1256.py"), - ("users", "0092_auto_20200502_0057.py"), - ("users", "0093_user_profile_image.py"), - ("users", "0094_remove_user_profile_image.py"), - ("users", "0095_user_theme.py"), + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), ] operations = [ migrations.CreateModel( From 392ea8afbcb32d7563abe75649e0b8abd20e2971 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 15:44:56 +0100 Subject: [PATCH 455/490] Fix dependecies graph creation. --- cotisations/migrations/0001_squashed_0050.py | 365 ++++++++++++++++++ machines/migrations/0001_squashed_0108.py | 309 ++++++++++++++- .../migrations/0063_auto_20171020_0040.py | 2 +- preferences/migrations/0001_squashed_0071.py | 333 +++++++++++++++- topologie/migrations/0001_squashed_0074.py | 340 ++++++++++++++++ users/migrations/0001_squashed_0095.py | 322 +++++++++++++++ 6 files changed, 1668 insertions(+), 3 deletions(-) diff --git a/cotisations/migrations/0001_squashed_0050.py b/cotisations/migrations/0001_squashed_0050.py index 4df7dc93..6a41936f 100644 --- a/cotisations/migrations/0001_squashed_0050.py +++ b/cotisations/migrations/0001_squashed_0050.py @@ -15,6 +15,103 @@ class Migration(migrations.Migration): initial = True dependencies = [] replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), @@ -66,6 +163,274 @@ class Migration(migrations.Migration): ("cotisations", "0049_auto_20201102_2305"), ("cotisations", "0050_auto_20201102_2342"), ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), ] operations = [ migrations.CreateModel( diff --git a/machines/migrations/0001_squashed_0108.py b/machines/migrations/0001_squashed_0108.py index 3341abf8..20a245d3 100644 --- a/machines/migrations/0001_squashed_0108.py +++ b/machines/migrations/0001_squashed_0108.py @@ -14,8 +14,155 @@ class Migration(migrations.Migration): initial = True dependencies = [] replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), ("machines", "0001_initial"), - ("machines", "0001_squashed_0108"), ("machines", "0002_auto_20160703_1444"), ("machines", "0003_auto_20160703_1450"), ("machines", "0004_auto_20160703_1451"), @@ -123,6 +270,166 @@ class Migration(migrations.Migration): ("machines", "0106_auto_20191120_0159"), ("machines", "0107_fix_lowercase_domain"), ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), ] operations = [ migrations.CreateModel( diff --git a/machines/migrations/0063_auto_20171020_0040.py b/machines/migrations/0063_auto_20171020_0040.py index 8b9b7d24..ea3b65a9 100644 --- a/machines/migrations/0063_auto_20171020_0040.py +++ b/machines/migrations/0063_auto_20171020_0040.py @@ -11,7 +11,7 @@ class Migration(migrations.Migration): dependencies = [ ("machines", "0062_extension_origin_v6"), - ("reversion", "0001_squashed_0004_auto_20160611_1202"), + #("reversion", "0001_squashed_0004_auto_20160611_1202"), ] operations = [ diff --git a/preferences/migrations/0001_squashed_0071.py b/preferences/migrations/0001_squashed_0071.py index 4137aaea..62f43ea1 100644 --- a/preferences/migrations/0001_squashed_0071.py +++ b/preferences/migrations/0001_squashed_0071.py @@ -9,8 +9,263 @@ import re2o.aes_field class Migration(migrations.Migration): dependencies = [] replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), ("preferences", "0001_initial"), - ("preferences", "0001_squashed_0071"), ("preferences", "0002_auto_20170625_1923"), ("preferences", "0003_optionaluser_solde_negatif"), ("preferences", "0004_assooption_services"), @@ -94,6 +349,82 @@ class Migration(migrations.Migration): ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), ("preferences", "0070_auto_20200419_0225"), ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), ] operations = [ migrations.CreateModel( diff --git a/topologie/migrations/0001_squashed_0074.py b/topologie/migrations/0001_squashed_0074.py index 6ed9ca8b..e338f3aa 100644 --- a/topologie/migrations/0001_squashed_0074.py +++ b/topologie/migrations/0001_squashed_0074.py @@ -14,6 +14,346 @@ class Migration(migrations.Migration): initial = True dependencies = [("machines", "0001_squashed_0108")] replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), ("topologie", "0001_initial"), ("topologie", "0002_auto_20160703_1118"), ("topologie", "0003_room"), diff --git a/users/migrations/0001_squashed_0095.py b/users/migrations/0001_squashed_0095.py index e9861e5d..61a37ce4 100644 --- a/users/migrations/0001_squashed_0095.py +++ b/users/migrations/0001_squashed_0095.py @@ -13,6 +13,9 @@ import users.models class Migration(migrations.Migration): dependencies = [] + initial=True + run_before = [('reversion', '0001_squashed_0004_auto_20160611_1202')] + # We replace everything. replaces = [ ("users", "0001_initial"), ("users", "0002_auto_20160630_2301"), @@ -111,6 +114,325 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), ] operations = [ migrations.CreateModel( From 937f2cf16c86c6abf3a9e1e3a87b88afe2cd0586 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 17:08:42 +0100 Subject: [PATCH 456/490] Fix miscellanous model creation mistakes. --- ...quashed_0050.py => 0001_model_creation.py} | 36 ++--- ...quashed_0108.py => 0001_model_creation.py} | 7 +- ...quashed_0071.py => 0001_model_creation.py} | 145 +++++++++++++----- ...quashed_0074.py => 0001_model_creation.py} | 27 +++- ...quashed_0095.py => 0001_model_creation.py} | 46 +++++- 5 files changed, 190 insertions(+), 71 deletions(-) rename cotisations/migrations/{0001_squashed_0050.py => 0001_model_creation.py} (97%) rename machines/migrations/{0001_squashed_0108.py => 0001_model_creation.py} (99%) rename preferences/migrations/{0001_squashed_0071.py => 0001_model_creation.py} (90%) rename topologie/migrations/{0001_squashed_0074.py => 0001_model_creation.py} (97%) rename users/migrations/{0001_squashed_0095.py => 0001_model_creation.py} (95%) diff --git a/cotisations/migrations/0001_squashed_0050.py b/cotisations/migrations/0001_model_creation.py similarity index 97% rename from cotisations/migrations/0001_squashed_0050.py rename to cotisations/migrations/0001_model_creation.py index 6a41936f..aa45127a 100644 --- a/cotisations/migrations/0001_squashed_0050.py +++ b/cotisations/migrations/0001_model_creation.py @@ -445,7 +445,7 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("date", models.DateTimeField(auto_now_add=True, verbose_name="Date")), + ("date", models.DateTimeField(auto_now_add=True, verbose_name="date")), ], bases=( re2o.mixins.RevMixin, @@ -506,14 +506,14 @@ class Migration(migrations.Migration): ), ( "recipient", - models.CharField(max_length=255, verbose_name="Recipient"), + models.CharField(max_length=255, verbose_name="recipient"), ), ( "payment", - models.CharField(max_length=255, verbose_name="Payment type"), + models.CharField(max_length=255, verbose_name="payment type"), ), - ("address", models.CharField(max_length=255, verbose_name="Address")), - ("paid", models.BooleanField(verbose_name="Paid")), + ("address", models.CharField(max_length=255, verbose_name="address")), + ("paid", models.BooleanField(default=False, verbose_name="paid")), ( "remark", models.TextField(verbose_name="remark", blank=True, null=True), @@ -521,7 +521,9 @@ class Migration(migrations.Migration): ], bases=("cotisations.baseinvoice",), options={ - "permissions": (("view_custominvoice", "Can view a custom invoice"),) + "permissions": ( + ("view_custominvoice", "Can view a custom invoice object"), + ) }, ), migrations.CreateModel( @@ -541,7 +543,7 @@ class Migration(migrations.Migration): ( "validity", models.DurationField( - verbose_name="Period of validity", help_text="DD HH:MM:SS" + verbose_name="period of validity", help_text="DD HH:MM:SS" ), ), ], @@ -588,7 +590,6 @@ class Migration(migrations.Migration): "duration_days_connection", models.PositiveIntegerField( default=0, - validators=[django.core.validators.MinValueValidator(0)], verbose_name="duration of the connection (in days, will be added to duration in months)", ), ), @@ -602,7 +603,6 @@ class Migration(migrations.Migration): "duration_days_membership", models.PositiveIntegerField( default=0, - validators=[django.core.validators.MinValueValidator(0)], verbose_name="duration of the membership (in days, will be added to duration in months)", ), ), @@ -651,28 +651,24 @@ class Migration(migrations.Migration): ( "duration_connection", models.PositiveIntegerField( - default=0, verbose_name="duration of the connection (in months)" + verbose_name="duration of the connection (in months)" ), ), ( "duration_days_connection", models.PositiveIntegerField( - default=0, - validators=[django.core.validators.MinValueValidator(0)], verbose_name="duration of the connection (in days, will be added to duration in months)", ), ), ( "duration_membership", models.PositiveIntegerField( - default=0, verbose_name="duration of the membership (in months)" + verbose_name="duration of the membership (in months)" ), ), ( "duration_days_membership", models.PositiveIntegerField( - default=0, - validators=[django.core.validators.MinValueValidator(0)], verbose_name="duration of the membership (in days, will be added to duration in months)", ), ), @@ -866,7 +862,7 @@ class Migration(migrations.Migration): ), ), ], - options={"verbose_name", "user balance"}, + options={"verbose_name": "user balance"}, ), migrations.CreateModel( name="ChequePayment", @@ -882,7 +878,7 @@ class Migration(migrations.Migration): ), ), ], - options={"verbose_name", "cheque"}, + options={"verbose_name": "cheque"}, ), migrations.CreateModel( name="ComnpayPayment", @@ -933,7 +929,7 @@ class Migration(migrations.Migration): ), ), ], - options={"verbose_name", "ComNpay"}, + options={"verbose_name": "ComNpay"}, ), migrations.CreateModel( name="FreePayment", @@ -949,7 +945,7 @@ class Migration(migrations.Migration): ), ), ], - options={"verbose_name", "Free payment"}, + options={"verbose_name": "Free payment"}, ), migrations.CreateModel( name="NotePayment", @@ -968,6 +964,6 @@ class Migration(migrations.Migration): ("port", models.PositiveIntegerField(blank=True, null=True)), ("id_note", models.PositiveIntegerField(blank=True, null=True)), ], - options={"verbose_name", "NoteKfet"}, + options={"verbose_name": "NoteKfet"}, ), ] diff --git a/machines/migrations/0001_squashed_0108.py b/machines/migrations/0001_model_creation.py similarity index 99% rename from machines/migrations/0001_squashed_0108.py rename to machines/migrations/0001_model_creation.py index 20a245d3..a16505a9 100644 --- a/machines/migrations/0001_squashed_0108.py +++ b/machines/migrations/0001_model_creation.py @@ -522,7 +522,7 @@ class Migration(migrations.Migration): protocol="IPv4", null=True, blank=True, - help_text="Network containing the domain's IPv4 range optional).", + help_text="Network containing the domain's IPv4 range (optional).", ), ), ( @@ -570,8 +570,8 @@ class Migration(migrations.Migration): ("view_iptype", "Can view an IP type object"), ("use_all_iptype", "Can use all IP types"), ), - "verbose_name": "Ip type", - "verbose_name_plural": "Ip types", + "verbose_name": "IP type", + "verbose_name_plural": "IP types", }, ), migrations.CreateModel( @@ -1108,7 +1108,6 @@ class Migration(migrations.Migration): ), ], options={ - "unique_together": (("name", "extension"),), "permissions": ( ("view_domain", "Can view a domain object"), ("change_ttl", "Can change the TTL of a domain object"), diff --git a/preferences/migrations/0001_squashed_0071.py b/preferences/migrations/0001_model_creation.py similarity index 90% rename from preferences/migrations/0001_squashed_0071.py rename to preferences/migrations/0001_model_creation.py index 62f43ea1..b3ece734 100644 --- a/preferences/migrations/0001_squashed_0071.py +++ b/preferences/migrations/0001_model_creation.py @@ -431,8 +431,10 @@ class Migration(migrations.Migration): name="OptionalUser", bases=(re2o.mixins.AclMixin, models.Model), options={ - "permissions": (("view_optionaluser", "Can view the user options"),), - "verbose_name": "user options", + "permissions": ( + ("view_optionaluser", "Can view the user preferences"), + ), + "verbose_name": "user preferences", }, fields=[ ( @@ -446,10 +448,30 @@ class Migration(migrations.Migration): ), ("is_tel_mandatory", models.BooleanField(default=True)), ("gpg_fingerprint", models.BooleanField(default=True)), - ("all_can_create_club", models.BooleanField(default=False)), - ("all_can_create_adherent", models.BooleanField(default=False)), - ("self_change_shell", models.BooleanField(default=False)), - ("self_change_pseudo", models.BooleanField(default=True)), + ( + "all_can_create_club", + models.BooleanField( + default=False, help_text="Users can create a club." + ), + ), + ( + "all_can_create_adherent", + models.BooleanField( + default=False, help_text="Users can create a member." + ), + ), + ( + "self_change_shell", + models.BooleanField( + default=False, help_text="Users can edit their shell." + ), + ), + ( + "self_change_pseudo", + models.BooleanField( + default=True, help_text="Users can edit their pseudo." + ), + ), ( "self_room_policy", models.CharField( @@ -466,7 +488,13 @@ class Migration(migrations.Migration): max_length=32, ), ), - ("local_email_accounts_enabled", models.BooleanField(default=False)), + ( + "local_email_accounts_enabled", + models.BooleanField( + default=False, + help_text="Enable local email accounts for users.", + ), + ), ( "local_email_domain", models.CharField( @@ -496,13 +524,34 @@ class Migration(migrations.Migration): help_text="Users with an email address not yet confirmed will be disabled after this number of days.", ), ), - ("self_adhesion", models.BooleanField(default=False)), - ("all_users_active", models.BooleanField(default=False)), + ( + "self_adhesion", + models.BooleanField( + default=False, + help_text="A new user can create their account on Re2o.", + ), + ), + ( + "all_users_active", + models.BooleanField( + default=False, + help_text="If True, all new created and connected users are active. If False, only when a valid registration has been paid.", + ), + ), ( "allow_set_password_during_user_creation", - models.BooleanField(default=False), + models.BooleanField( + default=False, + help_text="If True, users have the choice to receive an email containing a link to reset their password during creation, or to directly set their password in the page. If False, an email is always sent.", + ), + ), + ( + "allow_archived_connexion", + models.BooleanField( + default=False, + help_text="If True, archived users are allowed to connect.", + ), ), - ("allow_archived_connexion", models.BooleanField(default=False)), ], ), migrations.CreateModel( @@ -540,12 +589,13 @@ class Migration(migrations.Migration): verbose_name="default Time To Live (TTL) for CNAME, A and AAAA records", ), ), + ("max_lambdauser_aliases", models.IntegerField(default=10)), ], options={ "permissions": ( - ("view_optionalmachine", "Can view the machine options"), + ("view_optionalmachine", "Can view the machine preferences"), ), - "verbose_name": "machine options", + "verbose_name": "machine preferences", }, ), migrations.CreateModel( @@ -553,9 +603,9 @@ class Migration(migrations.Migration): bases=(re2o.mixins.AclMixin, models.Model), options={ "permissions": ( - ("view_optionaltopologie", "Can view the topology options"), + ("view_optionaltopologie", "Can view the topology preferences"), ), - "verbose_name": "topology options", + "verbose_name": "topology preferences", }, fields=[ ( @@ -567,9 +617,27 @@ class Migration(migrations.Migration): verbose_name="ID", ), ), - ("switchs_web_management", models.BooleanField(default=False)), - ("switchs_web_management_ssl", models.BooleanField(default=False)), - ("switchs_rest_management", models.BooleanField(default=False)), + ( + "switchs_web_management", + models.BooleanField( + default=False, + help_text="Web management, activated in case of automatic provision.", + ), + ), + ( + "switchs_web_management_ssl", + models.BooleanField( + default=False, + help_text="SSL web management, make sure that a certificate is installed on the switch.", + ), + ), + ( + "switchs_rest_management", + models.BooleanField( + default=False, + help_text="REST management, activated in case of automatic provision.", + ), + ), ( "switchs_provision", models.CharField( @@ -611,14 +679,14 @@ class Migration(migrations.Migration): ( "radius_key", re2o.aes_field.AESEncryptedField( - help_text="Clef radius", max_length=255 + help_text="RADIUS key.", max_length=255 ), ), ( "comment", models.CharField( blank=True, - help_text="Commentaire de cette clef", + help_text="Comment for this key.", max_length=255, null=True, ), @@ -626,9 +694,8 @@ class Migration(migrations.Migration): ( "default_switch", models.BooleanField( - default=True, - help_text="Clef par défaut des switchs", - unique=True, + default=False, + help_text="Default key for switches.", ), ), ], @@ -653,19 +720,19 @@ class Migration(migrations.Migration): ), ( "management_id", - models.CharField(help_text="Login du switch", max_length=63), + models.CharField(help_text="Switch login.", max_length=63), ), ( "management_pass", re2o.aes_field.AESEncryptedField( - help_text="Mot de passe", max_length=63 + help_text="Password.", max_length=63 ), ), ( "default_switch", models.BooleanField( default=True, - help_text="Creds par défaut des switchs", + help_text="Default credentials for switches.", unique=True, ), ), @@ -697,17 +764,16 @@ class Migration(migrations.Migration): "days", models.IntegerField( default=7, - help_text="Délais entre le mail et la fin d'adhésion", + help_text="Delay between the email and the membership's end.", unique=True, ), ), ( "message", - models.CharField( + models.TextField( blank=True, default="", - help_text="Message affiché spécifiquement pour ce rappel", - max_length=255, + help_text="Message displayed specifically for this reminder.", null=True, ), ), @@ -769,15 +835,15 @@ class Migration(migrations.Migration): ], options={ "permissions": ( - ("view_generaloption", "Can view the general options"), + ("view_generaloption", "Can view the general preferences"), ), - "verbose_name": "general options", + "verbose_name": "general preferences", }, ), migrations.CreateModel( name="Service", options={ - "permissions": (("view_service", "Can view the service options"),), + "permissions": (("view_service", "Can view the service preferences"),), "verbose_name": "service", "verbose_name_plural": "services", }, @@ -794,7 +860,7 @@ class Migration(migrations.Migration): ("name", models.CharField(max_length=32)), ("url", models.URLField()), ("description", models.TextField()), - ("image", models.ImageField(upload_to="logo")), + ("image", models.ImageField(blank=True, upload_to="logo")), ], ), migrations.CreateModel( @@ -813,7 +879,7 @@ class Migration(migrations.Migration): "address", models.EmailField( default="contact@example.org", - help_text="Contact email adress", + help_text="Contact email address.", max_length=254, ), ), @@ -821,7 +887,7 @@ class Migration(migrations.Migration): "commentary", models.CharField( blank=True, - help_text="Description of the associated email adress.", + help_text="Description of the associated email address.", max_length=256, null=True, ), @@ -857,9 +923,9 @@ class Migration(migrations.Migration): ), ], options={ - "verbose_name": "Mandate", - "verbose_name_plural": "Mandates", - "permissions": (("view_mandate", "Can view a mandate"),), + "verbose_name": "mandate", + "verbose_name_plural": "mandates", + "permissions": (("view_mandate", "Can view a mandate object"),), }, bases=(re2o.mixins.RevMixin, re2o.mixins.AclMixin, models.Model), ), @@ -1119,6 +1185,7 @@ class Migration(migrations.Migration): ), ), ], + options={"verbose_name": "subscription preferences"}, ), migrations.CreateModel( name="DocumentTemplate", diff --git a/topologie/migrations/0001_squashed_0074.py b/topologie/migrations/0001_model_creation.py similarity index 97% rename from topologie/migrations/0001_squashed_0074.py rename to topologie/migrations/0001_model_creation.py index e338f3aa..76b9992e 100644 --- a/topologie/migrations/0001_squashed_0074.py +++ b/topologie/migrations/0001_model_creation.py @@ -12,7 +12,7 @@ import re2o.field_permissions class Migration(migrations.Migration): initial = True - dependencies = [("machines", "0001_squashed_0108")] + dependencies = [("machines", "0001_model_creation")] replaces = [ ("users", "0001_initial"), ("users", "0002_auto_20160630_2301"), @@ -787,7 +787,7 @@ class Migration(migrations.Migration): options={ "permissions": (("view_port", "Can view a port object"),), "verbose_name": "port", - "verbose_name_plural": "port", + "verbose_name_plural": "ports", }, ), migrations.CreateModel( @@ -927,4 +927,27 @@ class Migration(migrations.Migration): "verbose_name_plural": "port profiles", }, ), + migrations.CreateModel( + name="Room", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255)), + ("details", models.CharField(blank=True, max_length=255)), + ], + options={ + "verbose_name": "room", + "verbose_name_plural": "rooms", + "ordering": ["building__name"], + "permissions": (("view_room", "Can view a room object"),), + }, + bases=(re2o.mixins.AclMixin, re2o.mixins.RevMixin, models.Model), + ), ] diff --git a/users/migrations/0001_squashed_0095.py b/users/migrations/0001_model_creation.py similarity index 95% rename from users/migrations/0001_squashed_0095.py rename to users/migrations/0001_model_creation.py index 61a37ce4..986f1f3f 100644 --- a/users/migrations/0001_squashed_0095.py +++ b/users/migrations/0001_model_creation.py @@ -12,9 +12,9 @@ import users.models class Migration(migrations.Migration): - dependencies = [] - initial=True - run_before = [('reversion', '0001_squashed_0004_auto_20160611_1202')] + dependencies = [('auth', '0008_alter_user_username_max_length')] + initial = True + run_before = [("reversion", "0001_squashed_0004_auto_20160611_1202")] # We replace everything. replaces = [ ("users", "0001_initial"), @@ -542,6 +542,28 @@ class Migration(migrations.Migration): ), ("email_change_date", models.DateTimeField(auto_now_add=True)), ("theme", models.CharField(max_length=255, default="default.css")), + ( + "is_superuser", + models.BooleanField( + default=False, + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", + ), + ), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "password", + models.CharField( + max_length=128, verbose_name="password" + ), + ), + ("groups", models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), + ("user_permissions", models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')) ], options={ "permissions": ( @@ -646,6 +668,18 @@ class Migration(migrations.Migration): "comment", models.CharField(help_text="Comment.", max_length=255, blank=True), ), + ( + "last_login", + models.DateTimeField( + blank=True, null=True, verbose_name="last login" + ), + ), + ( + "password", + models.CharField( + max_length=128, verbose_name="password" + ), + ), ], options={ "permissions": ( @@ -685,12 +719,12 @@ class Migration(migrations.Migration): ), fields=[ ( - "id", + "group_ptr", models.OneToOneField( - auto_created=True, - on_delete=django.db.models.deletion.CASCADE, parent_link=True, + auto_created=True, primary_key=True, + on_delete=django.db.models.deletion.CASCADE, serialize=False, to="auth.Group", ), From 5107a4987f706eaee0b8642c54bd6c9a673e3bdd Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 17:52:00 +0100 Subject: [PATCH 457/490] Foreign keys --- cotisations/migrations/0002_foreign_keys.py | 499 ++++++++++++++++ machines/migrations/0002_foreign_keys.py | 617 ++++++++++++++++++++ preferences/migrations/0002_foreign_keys.py | 531 +++++++++++++++++ topologie/migrations/0002_foreign_keys.py | 558 ++++++++++++++++++ users/migrations/0002_foreign_keys.py | 503 ++++++++++++++++ 5 files changed, 2708 insertions(+) create mode 100644 cotisations/migrations/0002_foreign_keys.py create mode 100644 machines/migrations/0002_foreign_keys.py create mode 100644 preferences/migrations/0002_foreign_keys.py create mode 100644 topologie/migrations/0002_foreign_keys.py create mode 100644 users/migrations/0002_foreign_keys.py diff --git a/cotisations/migrations/0002_foreign_keys.py b/cotisations/migrations/0002_foreign_keys.py new file mode 100644 index 00000000..0828aa5d --- /dev/null +++ b/cotisations/migrations/0002_foreign_keys.py @@ -0,0 +1,499 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 15:27 +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 = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('cotisations', '0001_model_creation'), + ] + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.AddField( + model_name='balancepayment', + name='payment', + field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_balance', to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='chequepayment', + name='payment', + field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_cheque', to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='comnpaypayment', + name='payment', + field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_comnpay', to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='costestimate', + name='final_invoice', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='origin_cost_estimate', to='cotisations.CustomInvoice'), + ), + migrations.AddField( + model_name='cotisation', + name='vente', + field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='purchase'), + ), + migrations.AddField( + model_name='facture', + name='banque', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cotisations.Banque'), + ), + migrations.AddField( + model_name='facture', + name='paiement', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='facture', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + migrations.AddField( + model_name='freepayment', + name='payment', + field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_free', to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='notepayment', + name='payment', + field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_note', to='cotisations.Paiement'), + preserve_default=False, + ), + migrations.AddField( + model_name='vente', + name='facture', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', verbose_name='invoice'), + preserve_default=False, + ), + ] diff --git a/machines/migrations/0002_foreign_keys.py b/machines/migrations/0002_foreign_keys.py new file mode 100644 index 00000000..4d864b22 --- /dev/null +++ b/machines/migrations/0002_foreign_keys.py @@ -0,0 +1,617 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 15:27 +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 = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('machines', '0001_model_creation'), + ] + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.AddField( + model_name='dname', + name='zone', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='domain', + name='extension', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='domain', + name='interface_parent', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='machines.Interface'), + ), + migrations.AddField( + model_name='extension', + name='origin', + field=models.ForeignKey(blank=True, help_text='A record associated with the zone.', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), + ), + migrations.AddField( + model_name='extension', + name='soa', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.SOA'), + preserve_default=False, + ), + migrations.AddField( + model_name='interface', + name='ipv4', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpList'), + ), + migrations.AddField( + model_name='interface', + name='machine', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.Machine'), + preserve_default=False, + ), + migrations.AddField( + model_name='interface', + name='machine_type', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.MachineType'), + preserve_default=False, + ), + migrations.AddField( + model_name='interface', + name='port_lists', + field=models.ManyToManyField(blank=True, to='machines.OuverturePortList'), + ), + migrations.AddField( + model_name='iplist', + name='ip_type', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.IpType'), + preserve_default=False, + ), + migrations.AddField( + model_name='iptype', + name='extension', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='iptype', + name='ouverture_ports', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'), + ), + migrations.AddField( + model_name='iptype', + name='vlan', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.Vlan'), + ), + migrations.AddField( + model_name='ipv6list', + name='interface', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='ipv6list', to='machines.Interface'), + preserve_default=False, + ), + migrations.AddField( + model_name='machine', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + migrations.AddField( + model_name='machinetype', + name='ip_type', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpType'), + ), + migrations.AddField( + model_name='mx', + name='name', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + preserve_default=False, + ), + migrations.AddField( + model_name='mx', + name='zone', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='nas', + name='machine_type', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='machinetype_on_nas', to='machines.MachineType'), + preserve_default=False, + ), + migrations.AddField( + model_name='nas', + name='nas_type', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, related_name='nas_type', to='machines.MachineType'), + preserve_default=False, + ), + migrations.AddField( + model_name='ns', + name='ns', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + preserve_default=False, + ), + migrations.AddField( + model_name='ns', + name='zone', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='ouvertureport', + name='port_list', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.OuverturePortList'), + preserve_default=False, + ), + migrations.AddField( + model_name='role', + name='servers', + field=models.ManyToManyField(to='machines.Interface'), + ), + migrations.AddField( + model_name='service', + name='servers', + field=models.ManyToManyField(through='machines.Service_link', to='machines.Interface'), + ), + migrations.AddField( + model_name='service_link', + name='server', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.Interface'), + preserve_default=False, + ), + migrations.AddField( + model_name='service_link', + name='service', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.Service'), + preserve_default=False, + ), + migrations.AddField( + model_name='srv', + name='extension', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AddField( + model_name='srv', + name='target', + field=models.ForeignKey(default=None, help_text='Target server.', on_delete=django.db.models.deletion.PROTECT, to='machines.Domain'), + preserve_default=False, + ), + migrations.AddField( + model_name='sshfp', + name='machine', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='machines.Machine'), + preserve_default=False, + ), + migrations.AddField( + model_name='txt', + name='zone', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='machines.Extension'), + preserve_default=False, + ), + migrations.AlterUniqueTogether( + name='domain', + unique_together=set([('name', 'extension')]), + ), + ] diff --git a/preferences/migrations/0002_foreign_keys.py b/preferences/migrations/0002_foreign_keys.py new file mode 100644 index 00000000..262b64bb --- /dev/null +++ b/preferences/migrations/0002_foreign_keys.py @@ -0,0 +1,531 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 15:27 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import preferences.models +import re2o.aes_field + + +class Migration(migrations.Migration): + + dependencies = [ + ('machines', '0001_model_creation'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('users', '0001_model_creation'), + ('preferences', '0001_model_creation'), + ('reversion', '0001_squashed_0004_auto_20160611_1202'), + ] + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.AddField( + model_name='assooption', + name='utilisateur_asso', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='cotisationsoption', + name='invoice_template', + field=models.OneToOneField(default=preferences.models.default_invoice, on_delete=django.db.models.deletion.PROTECT, related_name='invoice_template', to='preferences.DocumentTemplate', verbose_name='template for invoices'), + ), + migrations.AddField( + model_name='cotisationsoption', + name='voucher_template', + field=models.OneToOneField(default=preferences.models.default_voucher, on_delete=django.db.models.deletion.PROTECT, related_name='voucher_template', to='preferences.DocumentTemplate', verbose_name='template for subscription vouchers'), + ), + migrations.AddField( + model_name='mandate', + name='president', + field=models.ForeignKey(blank=True, help_text='Displayed on subscription vouchers.', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='president of the association'), + ), + migrations.AddField( + model_name='optionaltopologie', + name='switchs_ip_type', + field=models.OneToOneField(blank=True, help_text='IP range for the management of switches.', null=True, on_delete=django.db.models.deletion.PROTECT, to='machines.IpType'), + ), + migrations.AddField( + model_name='optionaluser', + name='shell_default', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='users.ListShell'), + ), + migrations.AddField( + model_name='radiusoption', + name='banned_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for banned users.', related_name='banned_attribute', to='preferences.RadiusAttribute', verbose_name='banned users attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='banned_vlan', + field=models.ForeignKey(blank=True, help_text='VLAN for banned users if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='banned_vlan', to='machines.Vlan', verbose_name='banned users VLAN'), + ), + migrations.AddField( + model_name='radiusoption', + name='non_member_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for non members.', related_name='non_member_attribute', to='preferences.RadiusAttribute', verbose_name='non members attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='non_member_vlan', + field=models.ForeignKey(blank=True, help_text='VLAN for non members if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='non_member_vlan', to='machines.Vlan', verbose_name='non members VLAN'), + ), + migrations.AddField( + model_name='radiusoption', + name='ok_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for accepted users.', related_name='ok_attribute', to='preferences.RadiusAttribute', verbose_name='accepted users attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_machine_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown machines.', related_name='unknown_machine_attribute', to='preferences.RadiusAttribute', verbose_name='unknown machines attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_machine_vlan', + field=models.ForeignKey(blank=True, help_text='VLAN for unknown machines if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_machine_vlan', to='machines.Vlan', verbose_name='unknown machines VLAN'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_port_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown ports.', related_name='unknown_port_attribute', to='preferences.RadiusAttribute', verbose_name='unknown ports attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_port_vlan', + field=models.ForeignKey(blank=True, help_text='VLAN for unknown ports if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_port_vlan', to='machines.Vlan', verbose_name='unknown ports VLAN'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_room_attributes', + field=models.ManyToManyField(blank=True, help_text='Answer attributes for unknown rooms.', related_name='unknown_room_attribute', to='preferences.RadiusAttribute', verbose_name='unknown rooms attributes'), + ), + migrations.AddField( + model_name='radiusoption', + name='unknown_room_vlan', + field=models.ForeignKey(blank=True, help_text='VLAN for unknown rooms if not rejected.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='unknown_room_vlan', to='machines.Vlan', verbose_name='unknown rooms VLAN'), + ), + migrations.AddField( + model_name='radiusoption', + name='vlan_decision_ok', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='vlan_ok_option', to='machines.Vlan'), + ), + ] diff --git a/topologie/migrations/0002_foreign_keys.py b/topologie/migrations/0002_foreign_keys.py new file mode 100644 index 00000000..492c97f6 --- /dev/null +++ b/topologie/migrations/0002_foreign_keys.py @@ -0,0 +1,558 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 15:27 +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', '0002_foreign_keys'), + ('preferences', '0001_model_creation'), + ('topologie', '0001_model_creation'), + ] + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.AddField( + model_name='building', + name='dormitory', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='topologie.Dormitory'), + preserve_default=False, + ), + migrations.AddField( + model_name='modelswitch', + name='constructor', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='topologie.ConstructorSwitch'), + preserve_default=False, + ), + migrations.AddField( + model_name='moduleonswitch', + name='module', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='topologie.ModuleSwitch'), + preserve_default=False, + ), + migrations.AddField( + model_name='moduleonswitch', + name='switch', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='topologie.Switch'), + preserve_default=False, + ), + migrations.AddField( + model_name='port', + name='custom_profile', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.PortProfile'), + ), + migrations.AddField( + model_name='port', + name='machine_interface', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='machines.Interface'), + ), + migrations.AddField( + model_name='port', + name='related', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='related_port', to='topologie.Port'), + ), + migrations.AddField( + model_name='port', + name='switch', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='ports', to='topologie.Switch'), + preserve_default=False, + ), + migrations.AddField( + model_name='portprofile', + name='on_dormitory', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dormitory_ofprofil', to='topologie.Dormitory', verbose_name='profile on dormitory'), + ), + migrations.AddField( + model_name='portprofile', + name='vlan_tagged', + field=models.ManyToManyField(blank=True, related_name='vlan_tagged', to='machines.Vlan', verbose_name='VLAN(s) tagged'), + ), + migrations.AddField( + model_name='portprofile', + name='vlan_untagged', + field=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'), + ), + migrations.AddField( + model_name='switch', + name='management_creds', + field=models.ForeignKey(blank=True, help_text='Management credentials for the switch.', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.SwitchManagementCred'), + ), + migrations.AddField( + model_name='switch', + name='model', + field=models.ForeignKey(blank=True, help_text='Switch model.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.ModelSwitch'), + ), + migrations.AddField( + model_name='switch', + name='radius_key', + field=models.ForeignKey(blank=True, help_text='RADIUS key of the switch.', null=True, on_delete=django.db.models.deletion.PROTECT, to='preferences.RadiusKey'), + ), + migrations.AddField( + model_name='switch', + name='stack', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.Stack'), + ), + migrations.AddField( + model_name='switch', + name='switchbay', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='topologie.SwitchBay'), + ), + migrations.AddField( + model_name='switchbay', + name='building', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='topologie.Building'), + preserve_default=False, + ), + migrations.AlterUniqueTogether( + name='moduleonswitch', + unique_together=set([('slot', 'switch')]), + ), + migrations.AddField( + model_name='port', + name='room', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), + ), + migrations.AlterUniqueTogether( + name='port', + unique_together=set([('switch', 'port')]), + ), + migrations.AlterUniqueTogether( + name='portprofile', + unique_together=set([('on_dormitory', 'profil_default')]), + ), + migrations.AlterUniqueTogether( + name='switch', + unique_together=set([('stack', 'stack_member_id')]), + ), + migrations.AddField( + model_name='room', + name='building', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='topologie.Building'), + ), + migrations.AlterUniqueTogether( + name='room', + unique_together=set([('name', 'building')]), + ), + ] diff --git a/users/migrations/0002_foreign_keys.py b/users/migrations/0002_foreign_keys.py new file mode 100644 index 00000000..7597b3c1 --- /dev/null +++ b/users/migrations/0002_foreign_keys.py @@ -0,0 +1,503 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 15:27 +from __future__ import unicode_literals + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import ldapdb.models.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0008_alter_user_username_max_length'), + ('topologie', '0001_model_creation'), + ('users', '0001_model_creation'), + ] + + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.AddField( + model_name='adherent', + name='room', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), + ), + migrations.AddField( + model_name='ban', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + migrations.AddField( + model_name='club', + name='administrators', + field=models.ManyToManyField(blank=True, related_name='club_administrator', to='users.Adherent'), + ), + migrations.AddField( + model_name='club', + name='members', + field=models.ManyToManyField(blank=True, related_name='club_members', to='users.Adherent'), + ), + migrations.AddField( + model_name='club', + name='room', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='topologie.Room'), + ), + migrations.AddField( + model_name='emailaddress', + name='user', + field=models.ForeignKey(default=None, help_text='User of the local email account.', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + migrations.AddField( + model_name='request', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + #migrations.AddField( + # model_name='user', + # name='groups', + # field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), + #), + migrations.AddField( + model_name='user', + name='school', + field=models.ForeignKey(blank=True, help_text='Education institute.', null=True, on_delete=django.db.models.deletion.PROTECT, to='users.School'), + ), + migrations.AddField( + model_name='user', + name='shell', + field=models.ForeignKey(blank=True, help_text='Unix shell.', null=True, on_delete=django.db.models.deletion.PROTECT, to='users.ListShell'), + ), + #migrations.AddField( + # model_name='user', + # name='user_permissions', + # field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), + #), + migrations.AddField( + model_name='whitelist', + name='user', + field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + preserve_default=False, + ), + ] From a73d23de5c6be2b2d7b4d03c158eb19e58020177 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 18:05:35 +0100 Subject: [PATCH 458/490] Tickets --- tickets/migrations/0001_squashed_0007.py | 81 ++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 tickets/migrations/0001_squashed_0007.py diff --git a/tickets/migrations/0001_squashed_0007.py b/tickets/migrations/0001_squashed_0007.py new file mode 100644 index 00000000..19c73679 --- /dev/null +++ b/tickets/migrations/0001_squashed_0007.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 16:53 +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): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + replaces = [ + ("tickets", "0001_initial"), + ("tickets", "0002_auto_20191120_0159"), + ("tickets", "0003_auto_20200422_1839"), + ("tickets", "0004_auto_20200422_2127"), + ("tickets", "0005_auto_20200422_2309"), + ("tickets", "0006_auto_20200423_0202"), + ("tickets", "0007_ticket_language"), + ] + + operations = [ + migrations.CreateModel( + name='CommentTicket', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('date', models.DateTimeField(auto_now_add=True)), + ('comment', models.TextField(max_length=4095)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='ticket_comment', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'ticket', + 'verbose_name_plural': 'tickets', + 'permissions': (('view_commentticket', 'Can view a ticket object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='Ticket', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(help_text='Title of the ticket.', max_length=255)), + ('description', models.TextField(max_length=3000)), + ('date', models.DateTimeField(auto_now_add=True)), + ('email', models.EmailField(help_text='An email address to get back to you.', max_length=100, null=True)), + ('solved', models.BooleanField(default=False)), + ('language', models.CharField(default='en', help_text='Language of the ticket.', max_length=16)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'ticket', + 'verbose_name_plural': 'tickets', + 'permissions': (('view_ticket', 'Can view a ticket object'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.CreateModel( + name='TicketOption', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('publish_address', models.EmailField(help_text='Email address to publish the new tickets (leave empty for no publication).', max_length=1000, null=True)), + ], + options={ + 'verbose_name': 'tickets options', + 'permissions': (('view_ticketoption', 'Can view tickets options'),), + }, + bases=(re2o.mixins.AclMixin, models.Model), + ), + migrations.AddField( + model_name='commentticket', + name='parent_ticket', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tickets.Ticket'), + ), + ] From d78c13361a56da25dc824324fd8a3764be35731d Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 18:35:00 +0100 Subject: [PATCH 459/490] multi_op --- .../0001_squashed_0003_auto_20200904_1938.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 multi_op/migrations/0001_squashed_0003_auto_20200904_1938.py diff --git a/multi_op/migrations/0001_squashed_0003_auto_20200904_1938.py b/multi_op/migrations/0001_squashed_0003_auto_20200904_1938.py new file mode 100644 index 00000000..2937f0c4 --- /dev/null +++ b/multi_op/migrations/0001_squashed_0003_auto_20200904_1938.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 17:32 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + replaces = [('multi_op', '0001_initial'), ('multi_op', '0002_auto_20200904_1905'), ('multi_op', '0003_auto_20200904_1938')] + + initial = True + + dependencies = [ + ('topologie', '0002_foreign_keys'), + ] + + operations = [ + migrations.CreateModel( + name='MultiopOption', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('enabled_dorm', models.ManyToManyField(blank=True, related_name='enabled_dorm_multiop', to='topologie.Dormitory', verbose_name='enabled dorm')), + ], + options={ + 'verbose_name': 'dormitories preferences', + }, + ), + ] From 1b4ec7915b9db6f204a8a5b9b12b7cf5992ab3e4 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 18:44:31 +0100 Subject: [PATCH 460/490] Fix #273 API import side-effect. --- api/acl.py | 25 ------------------------- api/migrations/0001_initial.py | 34 ++++++++++++++++++++++++++++++++++ api/migrations/__init__.py | 0 3 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 api/migrations/0001_initial.py create mode 100644 api/migrations/__init__.py diff --git a/api/acl.py b/api/acl.py index c745158f..e672338e 100644 --- a/api/acl.py +++ b/api/acl.py @@ -31,31 +31,6 @@ from django.contrib.contenttypes.models import ContentType from django.utils.translation import ugettext as _ -def _create_api_permission(): - """Creates the 'use_api' permission if not created. - - The 'use_api' is a fake permission in the sense it is not associated with an - existing model and this ensure the permission is created every time this file - is imported. - """ - api_content_type, created = ContentType.objects.get_or_create( - app_label=settings.API_CONTENT_TYPE_APP_LABEL, - model=settings.API_CONTENT_TYPE_MODEL, - ) - if created: - api_content_type.save() - api_permission, created = Permission.objects.get_or_create( - name=settings.API_PERMISSION_NAME, - content_type=api_content_type, - codename=settings.API_PERMISSION_CODENAME, - ) - if created: - api_permission.save() - - -#_create_api_permission() - - def can_view(user, *args, **kwargs): """Check if an user can view the application. diff --git a/api/migrations/0001_initial.py b/api/migrations/0001_initial.py new file mode 100644 index 00000000..44316324 --- /dev/null +++ b/api/migrations/0001_initial.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations +from django.conf import settings + +def create_api_permission(apps, schema_editor): + """Creates the 'use_api' permission if not created. + + The 'use_api' is a fake permission in the sense it is not associated with an + existing model and this ensure the permission is created. + """ + ContentType = apps.get_model("contenttypes", "ContentType") + Permission = apps.get_model("auth", "Permission") + api_content_type, created = ContentType.objects.get_or_create( + app_label=settings.API_CONTENT_TYPE_APP_LABEL, + model=settings.API_CONTENT_TYPE_MODEL, + ) + if created: + api_content_type.save() + api_permission, created = Permission.objects.get_or_create( + name=settings.API_PERMISSION_NAME, + content_type=api_content_type, + codename=settings.API_PERMISSION_CODENAME, + ) + if created: + api_permission.save() + +class Migration(migrations.Migration): + initial = True + dependencies = [] + operations = [ + migrations.RunPython(create_api_permission) + ] diff --git a/api/migrations/__init__.py b/api/migrations/__init__.py new file mode 100644 index 00000000..e69de29b From 83473aee84dbabd568bcdb20f3b25988b6875e53 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 19:09:25 +0100 Subject: [PATCH 461/490] LDAP --- ...serviceusergroup_ldapuser_ldapusergroup.py | 514 ++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100644 users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py diff --git a/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py b/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py new file mode 100644 index 00000000..8a960211 --- /dev/null +++ b/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py @@ -0,0 +1,514 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2020-12-30 17:47 +from __future__ import unicode_literals + +from django.db import migrations +import ldapdb.models.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0002_foreign_keys'), + ] + + replaces = [ + ("users", "0001_initial"), + ("users", "0002_auto_20160630_2301"), + ("users", "0003_listrights_rights"), + ("users", "0004_auto_20160701_2312"), + ("users", "0005_auto_20160702_0006"), + ("users", "0006_ban"), + ("users", "0007_auto_20160702_2322"), + ("users", "0008_user_registered"), + ("users", "0009_user_room"), + ("users", "0010_auto_20160703_1226"), + ("users", "0011_auto_20160703_1227"), + ("users", "0012_auto_20160703_1230"), + ("users", "0013_auto_20160704_1547"), + ("users", "0014_auto_20160704_1548"), + ("users", "0015_whitelist"), + ("users", "0016_auto_20160706_1220"), + ("users", "0017_auto_20160707_0105"), + ("users", "0018_auto_20160707_0115"), + ("users", "0019_auto_20160708_1633"), + ("users", "0020_request"), + ("users", "0021_ldapuser"), + ("users", "0022_ldapuser_sambasid"), + ("users", "0023_auto_20160724_1908"), + ("users", "0024_remove_ldapuser_mac_list"), + ("users", "0025_listshell"), + ("users", "0026_user_shell"), + ("users", "0027_auto_20160726_0216"), + ("users", "0028_auto_20160726_0227"), + ("users", "0029_auto_20160726_0229"), + ("users", "0030_auto_20160726_0357"), + ("users", "0031_auto_20160726_0359"), + ("users", "0032_auto_20160727_2122"), + ("users", "0033_remove_ldapuser_loginshell"), + ("users", "0034_auto_20161018_0037"), + ("users", "0035_auto_20161018_0046"), + ("users", "0036_auto_20161022_2146"), + ("users", "0037_auto_20161028_1906"), + ("users", "0038_auto_20161031_0258"), + ("users", "0039_auto_20161119_0033"), + ("users", "0040_auto_20161119_1709"), + ("users", "0041_listright_details"), + ("users", "0042_auto_20161126_2028"), + ("users", "0043_auto_20161224_1156"), + ("users", "0043_ban_state"), + ("users", "0044_user_ssh_public_key"), + ("users", "0045_merge"), + ("users", "0046_auto_20170617_1433"), + ("users", "0047_auto_20170618_0156"), + ("users", "0048_auto_20170618_0210"), + ("users", "0049_auto_20170618_1424"), + ("users", "0050_serviceuser_comment"), + ("users", "0051_user_telephone"), + ("users", "0052_ldapuser_shadowexpire"), + ("users", "0053_auto_20170626_2105"), + ("users", "0054_auto_20170626_2219"), + ("users", "0055_auto_20171003_0556"), + ("users", "0056_auto_20171015_2033"), + ("users", "0057_auto_20171023_0301"), + ("users", "0058_auto_20171025_0154"), + ("users", "0059_auto_20171025_1854"), + ("users", "0060_auto_20171120_0317"), + ("users", "0061_auto_20171230_2033"), + ("users", "0062_auto_20171231_0056"), + ("users", "0063_auto_20171231_0140"), + ("users", "0064_auto_20171231_0150"), + ("users", "0065_auto_20171231_2053"), + ("users", "0066_grouppermissions"), + ("users", "0067_serveurpermission"), + ("users", "0068_auto_20180107_2245"), + ("users", "0069_club_mailing"), + ("users", "0070_auto_20180324_1906"), + ("users", "0071_auto_20180415_1252"), + ("users", "0072_auto_20180426_2021"), + ("users", "0073_auto_20180629_1614"), + ("users", "0074_auto_20180810_2104"), + ("users", "0074_auto_20180814_1059"), + ("users", "0075_merge_20180815_2202"), + ("users", "0076_auto_20180818_1321"), + ("users", "0077_auto_20180824_1750"), + ("users", "0078_auto_20181011_1405"), + ("users", "0079_auto_20181228_2039"), + ("users", "0080_auto_20190108_1726"), + ("users", "0081_auto_20190317_0302"), + ("users", "0082_auto_20190908_1338"), + ("users", "0083_user_shortcuts_enabled"), + ("users", "0084_auto_20191120_0159"), + ("users", "0085_user_email_state"), + ("users", "0086_user_email_change_date"), + ("users", "0087_request_email"), + ("users", "0088_auto_20200417_2312"), + ("users", "0089_auto_20200418_0112"), + ("users", "0090_auto_20200421_1825"), + ("users", "0091_auto_20200423_1256"), + ("users", "0092_auto_20200502_0057"), + ("users", "0093_user_profile_image"), + ("users", "0094_remove_user_profile_image"), + ("users", "0095_user_theme"), + ("cotisations", "0001_initial"), + ("cotisations", "0002_remove_facture_article"), + ("cotisations", "0003_auto_20160702_1448"), + ("cotisations", "0004_auto_20160702_1528"), + ("cotisations", "0005_auto_20160702_1532"), + ("cotisations", "0006_auto_20160702_1534"), + ("cotisations", "0007_auto_20160702_1543"), + ("cotisations", "0008_auto_20160702_1614"), + ("cotisations", "0009_remove_cotisation_user"), + ("cotisations", "0010_auto_20160702_1840"), + ("cotisations", "0011_auto_20160702_1911"), + ("cotisations", "0012_auto_20160704_0118"), + ("cotisations", "0013_auto_20160711_2240"), + ("cotisations", "0014_auto_20160712_0245"), + ("cotisations", "0015_auto_20160714_2142"), + ("cotisations", "0016_auto_20160715_0110"), + ("cotisations", "0017_auto_20170718_2329"), + ("cotisations", "0018_paiement_type_paiement"), + ("cotisations", "0019_auto_20170819_0055"), + ("cotisations", "0020_auto_20170819_0057"), + ("cotisations", "0021_auto_20170819_0104"), + ("cotisations", "0022_auto_20170824_0128"), + ("cotisations", "0023_auto_20170902_1303"), + ("cotisations", "0024_auto_20171015_2033"), + ("cotisations", "0025_article_type_user"), + ("cotisations", "0026_auto_20171028_0126"), + ("cotisations", "0027_auto_20171029_1156"), + ("cotisations", "0028_auto_20171231_0007"), + ("cotisations", "0029_auto_20180414_2056"), + ("cotisations", "0030_custom_payment"), + ("cotisations", "0031_comnpaypayment_production"), + ("cotisations", "0032_custom_invoice"), + ("cotisations", "0033_auto_20180818_1319"), + ("cotisations", "0034_auto_20180831_1532"), + ("cotisations", "0035_notepayment"), + ("cotisations", "0036_custominvoice_remark"), + ("cotisations", "0037_costestimate"), + ("cotisations", "0038_auto_20181231_1657"), + ("cotisations", "0039_freepayment"), + ("cotisations", "0040_auto_20191002_2335"), + ("cotisations", "0041_auto_20191103_2131"), + ("cotisations", "0042_auto_20191120_0159"), + ("cotisations", "0043_separation_membership_connection_p1"), + ("cotisations", "0044_separation_membership_connection_p2"), + ("cotisations", "0045_separation_membership_connection_p3"), + ("cotisations", "0046_article_need_membership"), + ("cotisations", "0047_article_need_membership_init"), + ("cotisations", "0048_auto_20201017_0018"), + ("cotisations", "0049_auto_20201102_2305"), + ("cotisations", "0050_auto_20201102_2342"), + ("cotisations", "0051_auto_20201228_1636"), + ("machines", "0001_initial"), + ("machines", "0002_auto_20160703_1444"), + ("machines", "0003_auto_20160703_1450"), + ("machines", "0004_auto_20160703_1451"), + ("machines", "0005_auto_20160703_1523"), + ("machines", "0006_auto_20160703_1813"), + ("machines", "0007_auto_20160703_1816"), + ("machines", "0008_remove_interface_ipv6"), + ("machines", "0009_auto_20160703_2358"), + ("machines", "0010_auto_20160704_0104"), + ("machines", "0011_auto_20160704_0105"), + ("machines", "0012_auto_20160704_0118"), + ("machines", "0013_auto_20160705_1014"), + ("machines", "0014_auto_20160706_1220"), + ("machines", "0015_auto_20160707_0105"), + ("machines", "0016_auto_20160708_1633"), + ("machines", "0017_auto_20160708_1645"), + ("machines", "0018_auto_20160708_1813"), + ("machines", "0019_auto_20160718_1141"), + ("machines", "0020_auto_20160718_1849"), + ("machines", "0021_auto_20161006_1943"), + ("machines", "0022_auto_20161011_1829"), + ("machines", "0023_iplist_ip_type"), + ("machines", "0024_machinetype_need_infra"), + ("machines", "0025_auto_20161023_0038"), + ("machines", "0026_auto_20161026_1348"), + ("machines", "0027_alias"), + ("machines", "0028_iptype_domaine_ip"), + ("machines", "0029_iptype_domaine_range"), + ("machines", "0030_auto_20161118_1730"), + ("machines", "0031_auto_20161119_1709"), + ("machines", "0032_auto_20161119_1850"), + ("machines", "0033_extension_need_infra"), + ("machines", "0034_iplist_need_infra"), + ("machines", "0035_auto_20161224_1201"), + ("machines", "0036_auto_20161224_1204"), + ("machines", "0037_domain_cname"), + ("machines", "0038_auto_20161224_1721"), + ("machines", "0039_auto_20161224_1732"), + ("machines", "0040_remove_interface_dns"), + ("machines", "0041_remove_ns_interface"), + ("machines", "0042_ns_ns"), + ("machines", "0043_auto_20170721_0350"), + ("machines", "0044_auto_20170808_0233"), + ("machines", "0045_auto_20170808_0348"), + ("machines", "0046_auto_20170808_1423"), + ("machines", "0047_auto_20170809_0606"), + ("machines", "0048_auto_20170823_2315"), + ("machines", "0049_vlan"), + ("machines", "0050_auto_20170826_0022"), + ("machines", "0051_iptype_vlan"), + ("machines", "0052_auto_20170828_2322"), + ("machines", "0053_text"), + ("machines", "0054_text_zone"), + ("machines", "0055_nas"), + ("machines", "0056_nas_port_access_mode"), + ("machines", "0057_nas_autocapture_mac"), + ("machines", "0058_auto_20171002_0350"), + ("machines", "0059_iptype_prefix_v6"), + ("machines", "0060_iptype_ouverture_ports"), + ("machines", "0061_auto_20171015_2033"), + ("machines", "0062_extension_origin_v6"), + ("machines", "0063_auto_20171020_0040"), + ("machines", "0064_auto_20171115_0253"), + ("machines", "0065_auto_20171115_1514"), + ("machines", "0066_srv"), + ("machines", "0067_auto_20171116_0152"), + ("machines", "0068_auto_20171116_0252"), + ("machines", "0069_auto_20171116_0822"), + ("machines", "0070_auto_20171231_1947"), + ("machines", "0071_auto_20171231_2100"), + ("machines", "0072_auto_20180108_1822"), + ("machines", "0073_auto_20180128_2203"), + ("machines", "0074_auto_20180129_0352"), + ("machines", "0075_auto_20180130_0052"), + ("machines", "0076_auto_20180130_1623"), + ("machines", "0077_auto_20180409_2243"), + ("machines", "0078_auto_20180415_1252"), + ("machines", "0079_auto_20180416_0107"), + ("machines", "0080_auto_20180502_2334"), + ("machines", "0081_auto_20180521_1413"), + ("machines", "0082_auto_20180525_2209"), + ("machines", "0083_remove_duplicate_rights"), + ("machines", "0084_dname"), + ("machines", "0085_sshfingerprint"), + ("machines", "0086_role"), + ("machines", "0087_dnssec"), + ("machines", "0088_iptype_prefix_v6_length"), + ("machines", "0089_auto_20180805_1148"), + ("machines", "0090_auto_20180805_1459"), + ("machines", "0091_auto_20180806_2310"), + ("machines", "0092_auto_20180807_0926"), + ("machines", "0093_auto_20180807_1115"), + ("machines", "0094_auto_20180815_1918"), + ("machines", "0095_auto_20180919_2225"), + ("machines", "0096_auto_20181013_1417"), + ("machines", "0097_extension_dnssec"), + ("machines", "0098_auto_20190102_1745"), + ("machines", "0099_role_recursive_dns"), + ("machines", "0100_auto_20190102_1753"), + ("machines", "0101_auto_20190108_1623"), + ("machines", "0102_auto_20190303_1611"), + ("machines", "0103_auto_20191002_2222"), + ("machines", "0104_auto_20191002_2231"), + ("machines", "0105_dname_ttl"), + ("machines", "0106_auto_20191120_0159"), + ("machines", "0107_fix_lowercase_domain"), + ("machines", "0108_ipv6list_active"), + ("preferences", "0001_initial"), + ("preferences", "0002_auto_20170625_1923"), + ("preferences", "0003_optionaluser_solde_negatif"), + ("preferences", "0004_assooption_services"), + ("preferences", "0005_auto_20170824_0139"), + ("preferences", "0006_auto_20170824_0143"), + ("preferences", "0007_auto_20170824_2056"), + ("preferences", "0008_auto_20170824_2122"), + ("preferences", "0009_assooption_utilisateur_asso"), + ("preferences", "0010_auto_20170825_0459"), + ("preferences", "0011_auto_20170825_2307"), + ("preferences", "0012_generaloption_req_expire_hrs"), + ("preferences", "0013_generaloption_site_name"), + ("preferences", "0014_generaloption_email_from"), + ("preferences", "0015_optionaltopologie_radius_general_policy"), + ("preferences", "0016_auto_20170902_1520"), + ("preferences", "0017_mailmessageoption"), + ("preferences", "0018_optionaltopologie_mac_autocapture"), + ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), + ("preferences", "0020_optionalmachine_ipv6"), + ("preferences", "0021_auto_20171015_1741"), + ("preferences", "0022_auto_20171015_1758"), + ("preferences", "0023_auto_20171015_2033"), + ("preferences", "0024_optionaluser_all_can_create"), + ("preferences", "0025_auto_20171231_2142"), + ("preferences", "0025_generaloption_general_message"), + ("preferences", "0026_auto_20171216_0401"), + ("preferences", "0027_merge_20180106_2019"), + ("preferences", "0028_assooption_description"), + ("preferences", "0028_auto_20180111_1129"), + ("preferences", "0028_auto_20180128_2203"), + ("preferences", "0029_auto_20180111_1134"), + ("preferences", "0029_auto_20180318_0213"), + ("preferences", "0029_auto_20180318_1005"), + ("preferences", "0030_auto_20180111_2346"), + ("preferences", "0030_merge_20180320_1419"), + ("preferences", "0031_auto_20180323_0218"), + ("preferences", "0031_optionaluser_self_adhesion"), + ("preferences", "0032_optionaluser_min_online_payment"), + ("preferences", "0032_optionaluser_shell_default"), + ("preferences", "0033_accueiloption"), + ("preferences", "0033_generaloption_gtu_sum_up"), + ("preferences", "0034_auto_20180114_2025"), + ("preferences", "0034_auto_20180416_1120"), + ("preferences", "0035_auto_20180114_2132"), + ("preferences", "0035_optionaluser_allow_self_subscription"), + ("preferences", "0036_auto_20180114_2141"), + ("preferences", "0037_auto_20180114_2156"), + ("preferences", "0038_auto_20180114_2209"), + ("preferences", "0039_auto_20180115_0003"), + ("preferences", "0040_auto_20180129_1745"), + ("preferences", "0041_merge_20180130_0052"), + ("preferences", "0042_auto_20180222_1743"), + ("preferences", "0043_optionalmachine_create_machine"), + ("preferences", "0044_remove_payment_pass"), + ("preferences", "0045_remove_unused_payment_fields"), + ("preferences", "0046_optionaluser_mail_extension"), + ("preferences", "0047_mailcontact"), + ("preferences", "0048_auto_20180811_1515"), + ("preferences", "0049_optionaluser_self_change_shell"), + ("preferences", "0050_auto_20180818_1329"), + ("preferences", "0051_auto_20180919_2225"), + ("preferences", "0052_optionaluser_delete_notyetactive"), + ("preferences", "0053_optionaluser_self_change_room"), + ("preferences", "0055_generaloption_main_site_url"), + ("preferences", "0056_1_radiusoption"), + ("preferences", "0056_2_radiusoption"), + ("preferences", "0056_3_radiusoption"), + ("preferences", "0056_4_radiusoption"), + ("preferences", "0057_optionaluser_all_users_active"), + ("preferences", "0058_auto_20190108_1650"), + ("preferences", "0059_auto_20190120_1739"), + ("preferences", "0060_auto_20190712_1821"), + ("preferences", "0061_optionaluser_allow_archived_connexion"), + ("preferences", "0062_auto_20190910_1909"), + ("preferences", "0063_mandate"), + ("preferences", "0064_auto_20191008_1335"), + ("preferences", "0065_auto_20191010_1227"), + ("preferences", "0066_optionalmachine_default_dns_ttl"), + ("preferences", "0067_auto_20191120_0159"), + ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), + ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), + ("preferences", "0070_auto_20200419_0225"), + ("preferences", "0071_optionaluser_self_change_pseudo"), + ("topologie", "0001_initial"), + ("topologie", "0002_auto_20160703_1118"), + ("topologie", "0003_room"), + ("topologie", "0004_auto_20160703_1122"), + ("topologie", "0005_auto_20160703_1123"), + ("topologie", "0006_auto_20160703_1129"), + ("topologie", "0007_auto_20160703_1148"), + ("topologie", "0008_port_room"), + ("topologie", "0009_auto_20160703_1200"), + ("topologie", "0010_auto_20160704_2148"), + ("topologie", "0011_auto_20160704_2153"), + ("topologie", "0012_port_machine_interface"), + ("topologie", "0013_port_related"), + ("topologie", "0014_auto_20160706_1238"), + ("topologie", "0015_auto_20160706_1452"), + ("topologie", "0016_auto_20160706_1531"), + ("topologie", "0017_auto_20160718_1141"), + ("topologie", "0018_room_details"), + ("topologie", "0019_auto_20161026_1348"), + ("topologie", "0020_auto_20161119_0033"), + ("topologie", "0021_port_radius"), + ("topologie", "0022_auto_20161211_1622"), + ("topologie", "0023_auto_20170817_1654"), + ("topologie", "0023_auto_20170826_1530"), + ("topologie", "0024_auto_20170818_1021"), + ("topologie", "0024_auto_20170826_1800"), + ("topologie", "0025_merge_20170902_1242"), + ("topologie", "0026_auto_20170902_1245"), + ("topologie", "0027_auto_20170905_1442"), + ("topologie", "0028_auto_20170913_1503"), + ("topologie", "0029_auto_20171002_0334"), + ("topologie", "0030_auto_20171004_0235"), + ("topologie", "0031_auto_20171015_2033"), + ("topologie", "0032_auto_20171026_0338"), + ("topologie", "0033_auto_20171231_1743"), + ("topologie", "0034_borne"), + ("topologie", "0035_auto_20180324_0023"), + ("topologie", "0036_transferborne"), + ("topologie", "0037_auto_20180325_0127"), + ("topologie", "0038_transfersw"), + ("topologie", "0039_port_new_switch"), + ("topologie", "0040_transferports"), + ("topologie", "0041_transferportsw"), + ("topologie", "0042_transferswitch"), + ("topologie", "0043_renamenewswitch"), + ("topologie", "0044_auto_20180326_0002"), + ("topologie", "0045_auto_20180326_0123"), + ("topologie", "0046_auto_20180326_0129"), + ("topologie", "0047_ap_machine"), + ("topologie", "0048_ap_machine"), + ("topologie", "0049_switchs_machine"), + ("topologie", "0050_port_new_switch"), + ("topologie", "0051_switchs_machine"), + ("topologie", "0052_transferports"), + ("topologie", "0053_finalsw"), + ("topologie", "0054_auto_20180326_1742"), + ("topologie", "0055_auto_20180329_0431"), + ("topologie", "0056_building_switchbay"), + ("topologie", "0057_auto_20180408_0316"), + ("topologie", "0058_remove_switch_location"), + ("topologie", "0059_auto_20180415_2249"), + ("topologie", "0060_server"), + ("topologie", "0061_portprofile"), + ("topologie", "0062_auto_20180815_1918"), + ("topologie", "0063_auto_20180919_2225"), + ("topologie", "0064_switch_automatic_provision"), + ("topologie", "0065_auto_20180927_1836"), + ("topologie", "0066_modelswitch_commercial_name"), + ("topologie", "0067_auto_20181230_1819"), + ("topologie", "0068_auto_20190102_1758"), + ("topologie", "0069_auto_20190108_1439"), + ("topologie", "0070_auto_20190218_1743"), + ("topologie", "0071_auto_20190218_1936"), + ("topologie", "0072_auto_20190720_2318"), + ("topologie", "0073_auto_20191120_0159"), + ("topologie", "0074_auto_20200419_1640"), + ] + + operations = [ + migrations.CreateModel( + name='LdapServiceUser', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapServiceUserGroup', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('members', ldapdb.models.fields.ListField(blank=True, db_column='member')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapUser', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('uid', ldapdb.models.fields.CharField(db_column='uid', max_length=200)), + ('uidNumber', ldapdb.models.fields.IntegerField(db_column='uidNumber', unique=True)), + ('sn', ldapdb.models.fields.CharField(db_column='sn', max_length=200)), + ('login_shell', ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True)), + ('mail', ldapdb.models.fields.CharField(db_column='mail', max_length=200)), + ('given_name', ldapdb.models.fields.CharField(db_column='givenName', max_length=200)), + ('home_directory', ldapdb.models.fields.CharField(db_column='homeDirectory', max_length=200)), + ('display_name', ldapdb.models.fields.CharField(blank=True, db_column='displayName', max_length=200, null=True)), + ('dialupAccess', ldapdb.models.fields.CharField(db_column='dialupAccess', max_length=200)), + ('sambaSID', ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)), + ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), + ('sambat_nt_password', ldapdb.models.fields.CharField(blank=True, db_column='sambaNTPassword', max_length=200, null=True)), + ('macs', ldapdb.models.fields.ListField(blank=True, db_column='radiusCallingStationId', max_length=200, null=True)), + ('shadowexpire', ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapUserGroup', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), + ('members', ldapdb.models.fields.ListField(blank=True, db_column='memberUid')), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='ldapserviceuser', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapserviceusergroup', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapuser', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapusergroup', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + ] From 1833360d08381b169b10717d325e33f791581a92 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Wed, 30 Dec 2020 19:24:30 +0100 Subject: [PATCH 462/490] Clean migration --- machines/migrations/0063_auto_20171020_0040.py | 1 - 1 file changed, 1 deletion(-) diff --git a/machines/migrations/0063_auto_20171020_0040.py b/machines/migrations/0063_auto_20171020_0040.py index ea3b65a9..54d35980 100644 --- a/machines/migrations/0063_auto_20171020_0040.py +++ b/machines/migrations/0063_auto_20171020_0040.py @@ -11,7 +11,6 @@ class Migration(migrations.Migration): dependencies = [ ("machines", "0062_extension_origin_v6"), - #("reversion", "0001_squashed_0004_auto_20160611_1202"), ] operations = [ From f523118f3a50975bb8300a4d147a5d3f26fdc97c Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Thu, 31 Dec 2020 11:39:57 +0100 Subject: [PATCH 463/490] removed dead code. --- users/migrations/0002_foreign_keys.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/users/migrations/0002_foreign_keys.py b/users/migrations/0002_foreign_keys.py index 7597b3c1..7c471927 100644 --- a/users/migrations/0002_foreign_keys.py +++ b/users/migrations/0002_foreign_keys.py @@ -474,11 +474,6 @@ class Migration(migrations.Migration): field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), preserve_default=False, ), - #migrations.AddField( - # model_name='user', - # name='groups', - # field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'), - #), migrations.AddField( model_name='user', name='school', @@ -489,11 +484,6 @@ class Migration(migrations.Migration): name='shell', field=models.ForeignKey(blank=True, help_text='Unix shell.', null=True, on_delete=django.db.models.deletion.PROTECT, to='users.ListShell'), ), - #migrations.AddField( - # model_name='user', - # name='user_permissions', - # field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'), - #), migrations.AddField( model_name='whitelist', name='user', From 04b3d3b35f5112f722f4632fdb2e627d3c1b0437 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 10 Jan 2021 11:43:28 +0100 Subject: [PATCH 464/490] Fix migration conflict with reversion. --- users/migrations/0001_model_creation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/users/migrations/0001_model_creation.py b/users/migrations/0001_model_creation.py index 986f1f3f..3cefd817 100644 --- a/users/migrations/0001_model_creation.py +++ b/users/migrations/0001_model_creation.py @@ -14,7 +14,6 @@ import users.models class Migration(migrations.Migration): dependencies = [('auth', '0008_alter_user_username_max_length')] initial = True - run_before = [("reversion", "0001_squashed_0004_auto_20160611_1202")] # We replace everything. replaces = [ ("users", "0001_initial"), From 047d81eb91ffd85d2a350f8019744078997dc353 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 10 Jan 2021 20:15:46 +0100 Subject: [PATCH 465/490] docs: Create changelog for Re2o 2.9 We made it this far folks. Closes #307 Co-authored-by: Jean-Romain Garnier Co-authored-by: Hugo Levy-Falk --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 901f7b8e..41f47273 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,28 @@ +# Re2o 2.9 + +TODO after install: + +* !531: on the radius, add `buster-backports` to `/etc/apt/sources.list` and then run `apt update`, `apt install -t buster-backports freeradius`, `apt install python3-dev`, and `sudo pip3 install -r apt_requirements_radius.txt`. +* !582: run `sudo pip3 install -r pip_requirements.txt` and `python3 manage.py collectstatic`. +* `python3 manage.py migrate` and `sudo service apache2 reload` as usual. +* !589 : Add `ldap_sync` to your optional apps in your local settings if you want to keep using the LDAP synchronisation. + +New features: +* !488: Use `+` in search to combine keywords. +* !495: Add option to allow users to override another user's room, if that user is no longer active +* !496: Add option to allow users to choose password during account creation. They will have to separately confirm their email address. +* !513: IP and MAC address history (`Statistics > Machine history` tab) which also works for delete interfaces. Uses already existing history so events before the upgrade are taken into account. +* !516: Detail event in user's history view (e.g. show `old_email -> new_email`). +* !569: Refactor navbar to make menu navigation easier. +* !569: Add ability to have custom themes. +* !582: Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account). +* ?: Add option to choose minimum password length. +* ?: Add ability to comment on tickets. +* !578 : Migrations squashed to ease the installation process. First step towards making the LDAP an optional feature dor re2o. +* !507 : New form for editing list rights that should make everyone happier. +* !589 : Move LDAP stuff to an optional app. + +# Pevious to Re2o 2.9 ## MR 160: Datepicker Install libjs-jquery libjs-jquery-ui libjs-jquery-timepicker libjs-bootstrap javascript-common From ab4281bbf861830400f6ac7bd6371dc871249322 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 10 Jan 2021 20:30:57 +0100 Subject: [PATCH 466/490] fix: Minor change on changelog todo-list --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41f47273..01d2fc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ TODO after install: -* !531: on the radius, add `buster-backports` to `/etc/apt/sources.list` and then run `apt update`, `apt install -t buster-backports freeradius`, `apt install python3-dev`, and `sudo pip3 install -r apt_requirements_radius.txt`. +* !531: on the radius, add `buster-backports` to `/etc/apt/sources.list` and then run `apt update`, `apt install -t buster-backports freeradius`, `apt install python3-dev`, and `cat apt_requirements_radius.txt | xargs sudo apt -y install`. * !582: run `sudo pip3 install -r pip_requirements.txt` and `python3 manage.py collectstatic`. * `python3 manage.py migrate` and `sudo service apache2 reload` as usual. * !589 : Add `ldap_sync` to your optional apps in your local settings if you want to keep using the LDAP synchronisation. From 73c4d53ae04f3a5411d0ec69a1caa875c01b7732 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 11 Jan 2021 21:20:49 +0100 Subject: [PATCH 467/490] Reformat changelog for version 2.9 --- CHANGELOG.md | 89 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01d2fc00..e73612dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,28 +1,71 @@ # Re2o 2.9 -TODO after install: +## Install steps -* !531: on the radius, add `buster-backports` to `/etc/apt/sources.list` and then run `apt update`, `apt install -t buster-backports freeradius`, `apt install python3-dev`, and `cat apt_requirements_radius.txt | xargs sudo apt -y install`. -* !582: run `sudo pip3 install -r pip_requirements.txt` and `python3 manage.py collectstatic`. -* `python3 manage.py migrate` and `sudo service apache2 reload` as usual. -* !589 : Add `ldap_sync` to your optional apps in your local settings if you want to keep using the LDAP synchronisation. +To install the latest version of Re2o, checkout the [dedicated wiki entry](https://gitlab.federez.net/re2o/re2o/-/wikis/User-Documentation/Quick-Start#update-re2o). -New features: -* !488: Use `+` in search to combine keywords. -* !495: Add option to allow users to override another user's room, if that user is no longer active -* !496: Add option to allow users to choose password during account creation. They will have to separately confirm their email address. -* !513: IP and MAC address history (`Statistics > Machine history` tab) which also works for delete interfaces. Uses already existing history so events before the upgrade are taken into account. -* !516: Detail event in user's history view (e.g. show `old_email -> new_email`). -* !569: Refactor navbar to make menu navigation easier. -* !569: Add ability to have custom themes. -* !582: Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account). -* ?: Add option to choose minimum password length. -* ?: Add ability to comment on tickets. -* !578 : Migrations squashed to ease the installation process. First step towards making the LDAP an optional feature dor re2o. -* !507 : New form for editing list rights that should make everyone happier. -* !589 : Move LDAP stuff to an optional app. +## Post-install steps + +### MR 531: FreeRADIUS Python3 backend + +On the Radius server, add `buster-backports` to your `/etc/apt/sources.list`: +```bash +echo "deb http://deb.debian.org/debian buster-backports main contrib" >> /etc/apt/sources.list +``` + +**Note:** If you are running Debian Bullseye, the package should already be available without going through backports. + +Then install the new required packages: +```bash +apt update +apt install -t buster-backports freeradius +cat apt_requirements_radius.txt | xargs sudo apt -y install +``` + +### MR 582: Autocomplete light + +On the Re2o server, install the new dependency and run `collectstatic`: +```bash +sudo pip3 install -r pip_requirements.txt +python3 manage.py collectstatic +``` + +### MR 589: Move ldap to optional app + +Add `ldap_sync` to your optional apps in your local settings if you want to keep using the LDAP synchronisation. + +### Final steps + +As usual, run the following commands after updating: +```bash +python3 manage.py migrate +sudo service apache2 reload +``` + +## New features + +Here is a list of noteworthy features brought by this update: + +* [!488](https://gitlab.federez.net/re2o/re2o/-/merge_requests/488): Use `+` in searches to combine keywords (e.g. `John+Doe`). +* [!495](https://gitlab.federez.net/re2o/re2o/-/merge_requests/495): Add optional behavior allowing users to override another user's room, if that user is no longer active. +* [!496](https://gitlab.federez.net/re2o/re2o/-/merge_requests/496): Add option to allow users to choose their password during account creation. They will have to separately confirm their email address. +* [!504](https://gitlab.federez.net/re2o/re2o/-/merge_requests/504): Add setting to change the minimum password length. +* [!507](https://gitlab.federez.net/re2o/re2o/-/merge_requests/507): New form for editing lists of rights that should make everyone happier. +* [!512](https://gitlab.federez.net/re2o/re2o/-/merge_requests/512): Add ability to comment on tickets. +* [!513](https://gitlab.federez.net/re2o/re2o/-/merge_requests/513): IP and MAC address history (`Statistics > Machine history` tab) which also works for deleted interfaces. Uses already existing history so events before the upgrade are taken into account. +* [!516](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Detailed events in history views (e.g. show `old_email -> new_email`). +* [!519](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Add ability to filter event logs (e.g. to show all the subscriptions added by an admin). +* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Refactor navbar to make menu navigation easier. +* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Add ability to install custom themes. +* [!578](https://gitlab.federez.net/re2o/re2o/-/merge_requests/578) : Migrations squashed to ease the installation process. +* [!582](https://gitlab.federez.net/re2o/re2o/-/merge_requests/582): Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account). +* [!589](https://gitlab.federez.net/re2o/re2o/-/merge_requests/589): Move LDAP to a separate optional app. +* Plenty of bux fixes. + +You can view the full list of closed issues [here](https://gitlab.federez.net/re2o/re2o/-/issues?scope=all&state=all&milestone_title=Re2o 2.9). + +# Before Re2o 2.9 -# Pevious to Re2o 2.9 ## MR 160: Datepicker Install libjs-jquery libjs-jquery-ui libjs-jquery-timepicker libjs-bootstrap javascript-common @@ -46,7 +89,6 @@ rm static_files/js/jquery-2.2.4.min.js rm static/css/jquery-ui-timepicker-addon.css ``` - ## MR 159: Graph topo & MR 164: branche de création de graph Add a graph of the network topology @@ -59,7 +101,6 @@ Create the *media/images* directory: mkdir -p media/images ``` - ## MR 163: Fix install re2o Refactored install_re2o.sh script. @@ -70,8 +111,6 @@ install_re2o.sh help * The installation templates (LDIF files and `re2o/settings_locale.example.py`) have been changed to use `example.net` instead of `example.org` (more neutral and generic) - - ## MR 176: Add awesome Logo Add the logo and fix somme issues on the navbar and home page. Only collecting the statics is needed: @@ -79,7 +118,6 @@ Add the logo and fix somme issues on the navbar and home page. Only collecting t python3 manage.py collectstatic ``` - ## MR 172: Refactor API Creates a new (nearly) REST API to expose all models of Re2o. See [the dedicated wiki page](https://gitlab.federez.net/federez/re2o/wikis/API/Raw-Usage) for more details on how to use it. @@ -100,7 +138,6 @@ OPTIONAL_APPS = ( ) ``` - ## MR 177: Add django-debug-toolbar support Add the possibility to enable `django-debug-toolbar` in debug mode. First install the APT package: From 9b6ece28ce2d71552f52413855f35009ed8cea11 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 11 Jan 2021 21:22:29 +0100 Subject: [PATCH 468/490] Fix link in CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e73612dd..a3bf2a23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,7 +54,7 @@ Here is a list of noteworthy features brought by this update: * [!512](https://gitlab.federez.net/re2o/re2o/-/merge_requests/512): Add ability to comment on tickets. * [!513](https://gitlab.federez.net/re2o/re2o/-/merge_requests/513): IP and MAC address history (`Statistics > Machine history` tab) which also works for deleted interfaces. Uses already existing history so events before the upgrade are taken into account. * [!516](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Detailed events in history views (e.g. show `old_email -> new_email`). -* [!519](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Add ability to filter event logs (e.g. to show all the subscriptions added by an admin). +* [!519](https://gitlab.federez.net/re2o/re2o/-/merge_requests/519): Add ability to filter event logs (e.g. to show all the subscriptions added by an admin). * [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Refactor navbar to make menu navigation easier. * [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Add ability to install custom themes. * [!578](https://gitlab.federez.net/re2o/re2o/-/merge_requests/578) : Migrations squashed to ease the installation process. From 11028140d9c1757763a68dfc258d3b9e5315efa7 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 10 Jan 2021 17:49:23 +0100 Subject: [PATCH 469/490] feat: Move LDAP to an optional app. The Entire LDAP infrastructures now relies on signals rather than direct function calls and is in its own app. This means it can be deactivated, but also that we can easily plug new services in addition to LDAP, such as OAuth. Closes issue #270 --- ldap_sync/__init__.py | 0 ldap_sync/admin.py | 64 +++ ldap_sync/apps.py | 5 + .../management/commands/ldap_rebuild.py | 6 +- .../management/commands/ldap_sync.py | 6 +- ldap_sync/migrations/0001_initial.py | 108 +++++ ldap_sync/migrations/__init__.py | 0 ldap_sync/models.py | 334 ++++++++++++++ ldap_sync/tests.py | 3 + ldap_sync/urls.py | 4 + ldap_sync/views.py | 3 + re2o/context_processors.py | 4 +- users/admin.py | 59 --- users/migrations/0004_auto_20210110_1811.py | 27 ++ users/models.py | 435 ++---------------- users/signals.py | 33 ++ 16 files changed, 622 insertions(+), 469 deletions(-) create mode 100644 ldap_sync/__init__.py create mode 100644 ldap_sync/admin.py create mode 100644 ldap_sync/apps.py rename {users => ldap_sync}/management/commands/ldap_rebuild.py (94%) rename {users => ldap_sync}/management/commands/ldap_sync.py (88%) create mode 100644 ldap_sync/migrations/0001_initial.py create mode 100644 ldap_sync/migrations/__init__.py create mode 100644 ldap_sync/models.py create mode 100644 ldap_sync/tests.py create mode 100644 ldap_sync/urls.py create mode 100644 ldap_sync/views.py create mode 100644 users/migrations/0004_auto_20210110_1811.py create mode 100644 users/signals.py diff --git a/ldap_sync/__init__.py b/ldap_sync/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ldap_sync/admin.py b/ldap_sync/admin.py new file mode 100644 index 00000000..0cba55da --- /dev/null +++ b/ldap_sync/admin.py @@ -0,0 +1,64 @@ +from django.contrib import admin + +from .models import ( + LdapUser, + LdapServiceUser, + LdapServiceUserGroup, + LdapUserGroup, +) + +class LdapUserAdmin(admin.ModelAdmin): + """LdapUser Admin view. Can't change password, manage + by User General model. + + Parameters: + Django ModelAdmin: Apply on django ModelAdmin + + """ + list_display = ("name", "uidNumber", "login_shell") + exclude = ("user_password", "sambat_nt_password") + search_fields = ("name",) + + +class LdapServiceUserAdmin(admin.ModelAdmin): + """LdapServiceUser Admin view. Can't change password, manage + by User General model. + + Parameters: + Django ModelAdmin: Apply on django ModelAdmin + + """ + + list_display = ("name",) + exclude = ("user_password",) + search_fields = ("name",) + + +class LdapUserGroupAdmin(admin.ModelAdmin): + """LdapUserGroup Admin view. + + Parameters: + Django ModelAdmin: Apply on django ModelAdmin + + """ + + list_display = ("name", "members", "gid") + search_fields = ("name",) + + +class LdapServiceUserGroupAdmin(admin.ModelAdmin): + """LdapServiceUserGroup Admin view. + + Parameters: + Django ModelAdmin: Apply on django ModelAdmin + + """ + + list_display = ("name",) + search_fields = ("name",) + + +admin.site.register(LdapUser, LdapUserAdmin) +admin.site.register(LdapUserGroup, LdapUserGroupAdmin) +admin.site.register(LdapServiceUser, LdapServiceUserAdmin) +admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin) diff --git a/ldap_sync/apps.py b/ldap_sync/apps.py new file mode 100644 index 00000000..b96c34d1 --- /dev/null +++ b/ldap_sync/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class LdapSyncConfig(AppConfig): + name = 'ldap_sync' diff --git a/users/management/commands/ldap_rebuild.py b/ldap_sync/management/commands/ldap_rebuild.py similarity index 94% rename from users/management/commands/ldap_rebuild.py rename to ldap_sync/management/commands/ldap_rebuild.py index c8b172f9..1fc3c969 100644 --- a/users/management/commands/ldap_rebuild.py +++ b/ldap_sync/management/commands/ldap_rebuild.py @@ -1,4 +1,5 @@ # Copyright © 2018 Maël Kervella +# Copyright © 2021 Hugo Levy-Falk # # 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 @@ -21,6 +22,7 @@ from django.core.management.base import BaseCommand, CommandError from django.conf import settings from users.models import User, ListRight +from ldap_sync.models import synchronise_user, synchronise_serviceuser, synchronise_usergroup def split_lines(lines): @@ -89,9 +91,9 @@ def flush_ldap(binddn, bindpass, server, usersdn, groupsdn): def sync_ldap(): """Syncrhonize the whole LDAP with the DB.""" for u in User.objects.all(): - u.ldap_sync() + synchronise_user(sender=User, instance=u) for lr in ListRight.objects.all(): - lr.ldap_sync() + synchronise_usergroup(sender=ListRight, instance=lr) class Command(BaseCommand): diff --git a/users/management/commands/ldap_sync.py b/ldap_sync/management/commands/ldap_sync.py similarity index 88% rename from users/management/commands/ldap_sync.py rename to ldap_sync/management/commands/ldap_sync.py index 73f6698e..984f3fd7 100644 --- a/users/management/commands/ldap_sync.py +++ b/ldap_sync/management/commands/ldap_sync.py @@ -1,6 +1,7 @@ # Copyright © 2017 Gabriel Détraz # Copyright © 2017 Lara Kermarec # Copyright © 2017 Augustin Lemesle +# Copyright © 2020 Hugo Levy-Falk # # 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 @@ -19,6 +20,7 @@ from django.core.management.base import BaseCommand, CommandError from users.models import User +from ldap_sync.models import synchronise_user class Command(BaseCommand): @@ -36,5 +38,5 @@ class Command(BaseCommand): ) def handle(self, *args, **options): - for usr in User.objects.all(): - usr.ldap_sync(mac_refresh=options["full"]) + for user in User.objects.all(): + synchronise_user(sender=User, instance=user) diff --git a/ldap_sync/migrations/0001_initial.py b/ldap_sync/migrations/0001_initial.py new file mode 100644 index 00000000..5a4c448e --- /dev/null +++ b/ldap_sync/migrations/0001_initial.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2021-01-10 16:59 +from __future__ import unicode_literals + +from django.db import migrations +#from django.conf import settings +import ldapdb.models.fields + +#from ldap_sync.management.commands.ldap_rebuild import flush_ldap, sync_ldap + +#def rebuild_ldap(apps, schema_editor): +# usersdn = settings.LDAP["base_user_dn"] +# groupsdn = settings.LDAP["base_usergroup_dn"] +# binddn = settings.DATABASES["ldap"]["USER"] +# bindpass = settings.DATABASES["ldap"]["PASSWORD"] +# server = settings.DATABASES["ldap"]["NAME"] +# flush_ldap(binddn, bindpass, server, usersdn, groupsdn) + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('users', '0004_auto_20210110_1811') + ] + + operations = [ + migrations.CreateModel( + name='LdapServiceUser', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapServiceUserGroup', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('members', ldapdb.models.fields.ListField(blank=True, db_column='member')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapUser', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ('uid', ldapdb.models.fields.CharField(db_column='uid', max_length=200)), + ('uidNumber', ldapdb.models.fields.IntegerField(db_column='uidNumber', unique=True)), + ('sn', ldapdb.models.fields.CharField(db_column='sn', max_length=200)), + ('login_shell', ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True)), + ('mail', ldapdb.models.fields.CharField(db_column='mail', max_length=200)), + ('given_name', ldapdb.models.fields.CharField(db_column='givenName', max_length=200)), + ('home_directory', ldapdb.models.fields.CharField(db_column='homeDirectory', max_length=200)), + ('display_name', ldapdb.models.fields.CharField(blank=True, db_column='displayName', max_length=200, null=True)), + ('dialupAccess', ldapdb.models.fields.CharField(db_column='dialupAccess', max_length=200)), + ('sambaSID', ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)), + ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), + ('sambat_nt_password', ldapdb.models.fields.CharField(blank=True, db_column='sambaNTPassword', max_length=200, null=True)), + ('macs', ldapdb.models.fields.ListField(blank=True, db_column='radiusCallingStationId', max_length=200, null=True)), + ('shadowexpire', ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='LdapUserGroup', + fields=[ + ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), + ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), + ('members', ldapdb.models.fields.ListField(blank=True, db_column='memberUid')), + ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='ldapserviceuser', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapserviceusergroup', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapuser', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + migrations.AlterField( + model_name='ldapusergroup', + name='dn', + field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), + ), + ] diff --git a/ldap_sync/migrations/__init__.py b/ldap_sync/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ldap_sync/models.py b/ldap_sync/models.py new file mode 100644 index 00000000..b360e7c1 --- /dev/null +++ b/ldap_sync/models.py @@ -0,0 +1,334 @@ +import sys + +from django.db import models +from django.conf import settings +from django.dispatch import receiver + +from django.contrib.auth.models import Group + +import ldapdb.models +import ldapdb.models.fields + +import users.signals +import users.models + +import machines.models + +class LdapUser(ldapdb.models.Model): + """A class representing a LdapUser in LDAP, its LDAP conterpart. + Synced from re2o django User model, (User django models), + with a copy of its attributes/fields into LDAP, so this class is a mirror + of the classic django User model. + + The basedn userdn is specified in settings. + + Attributes: + name: The name of this User + uid: The uid (login) for the unix user + uidNumber: Linux uid number + gid: The default gid number for this user + sn: The user "str" pseudo + login_shell: Linux shell for the user + mail: Email address contact for this user + display_name: Pretty display name for this user + dialupAccess: Boolean, True for valid membership + sambaSID: Identical id as uidNumber + user_password: SSHA hashed password of user + samba_nt_password: NTLM hashed password of user + macs: Multivalued mac address + shadowexpire: Set it to 0 to block access for this user and disabled + account + """ + + # LDAP meta-data + base_dn = settings.LDAP["base_user_dn"] + object_classes = [ + "inetOrgPerson", + "top", + "posixAccount", + "sambaSamAccount", + "radiusprofile", + "shadowAccount", + ] + + # attributes + gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") + name = ldapdb.models.fields.CharField( + db_column="cn", max_length=200, primary_key=True + ) + uid = ldapdb.models.fields.CharField(db_column="uid", max_length=200) + uidNumber = ldapdb.models.fields.IntegerField(db_column="uidNumber", unique=True) + sn = ldapdb.models.fields.CharField(db_column="sn", max_length=200) + login_shell = ldapdb.models.fields.CharField( + db_column="loginShell", max_length=200, blank=True, null=True + ) + mail = ldapdb.models.fields.CharField(db_column="mail", max_length=200) + given_name = ldapdb.models.fields.CharField(db_column="givenName", max_length=200) + home_directory = ldapdb.models.fields.CharField( + db_column="homeDirectory", max_length=200 + ) + display_name = ldapdb.models.fields.CharField( + db_column="displayName", max_length=200, blank=True, null=True + ) + dialupAccess = ldapdb.models.fields.CharField(db_column="dialupAccess") + sambaSID = ldapdb.models.fields.IntegerField(db_column="sambaSID", unique=True) + user_password = ldapdb.models.fields.CharField( + db_column="userPassword", max_length=200, blank=True, null=True + ) + sambat_nt_password = ldapdb.models.fields.CharField( + db_column="sambaNTPassword", max_length=200, blank=True, null=True + ) + macs = ldapdb.models.fields.ListField( + db_column="radiusCallingStationId", max_length=200, blank=True, null=True + ) + shadowexpire = ldapdb.models.fields.CharField( + db_column="shadowExpire", blank=True, null=True + ) + + def __str__(self): + return self.name + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + self.sn = self.name + self.uid = self.name + self.sambaSID = self.uidNumber + super(LdapUser, self).save(*args, **kwargs) + + +@receiver(users.signals.synchronise, sender=users.models.User) +def synchronise_user(sender, **kwargs): + """ + Synchronise an User to the LDAP. + Args: + * sender : The model class. + * instance : The actual instance being synchronised. + * base : Default `True`. When `True`, synchronise basic attributes. + * access_refresh : Default `True`. When `True`, synchronise the access time. + * mac_refresh : Default `True`. When True, synchronise the list of mac addresses. + * group_refresh: Default `False`. When `True` synchronise the groups of the instance. + """ + base=kwargs.get('base', True) + access_refresh=kwargs.get('access_refresh', True) + mac_refresh=kwargs.get('mac_refresh', True ) + group_refresh=kwargs.get('group_refresh', False) + + user=kwargs["instance"] + + if sys.version_info[0] >= 3 and ( + user.state == user.STATE_ACTIVE + or user.state == user.STATE_ARCHIVE + or user.state == user.STATE_DISABLED + ): + user.refresh_from_db() + try: + user_ldap = LdapUser.objects.get(uidNumber=user.uid_number) + except LdapUser.DoesNotExist: + user_ldap = LdapUser(uidNumber=user.uid_number) + base = True + access_refresh = True + mac_refresh = True + if base: + user_ldap.name = user.pseudo + user_ldap.sn = user.pseudo + user_ldap.dialupAccess = str(user.has_access()) + user_ldap.home_directory = user.home_directory + user_ldap.mail = user.get_mail + user_ldap.given_name = ( + user.surname.lower() + "_" + user.name.lower()[:3] + ) + user_ldap.gid = settings.LDAP["user_gid"] + if "{SSHA}" in user.password or "{SMD5}" in user.password: + # We remove the extra $ added at import from ldap + user_ldap.user_password = user.password[:6] + user.password[7:] + elif "{crypt}" in user.password: + # depending on the length, we need to remove or not a $ + if len(user.password) == 41: + user_ldap.user_password = user.password + else: + user_ldap.user_password = user.password[:7] + user.password[8:] + + user_ldap.sambat_nt_password = user.pwd_ntlm.upper() + if user.get_shell: + user_ldap.login_shell = str(user.get_shell) + user_ldap.shadowexpire = user.get_shadow_expire + if access_refresh: + user_ldap.dialupAccess = str(user.has_access()) + if mac_refresh: + user_ldap.macs = [ + str(mac) + for mac in machines.models.Interface.objects.filter(machine__user=user) + .values_list("mac_address", flat=True) + .distinct() + ] + if group_refresh: + # Need to refresh all groups because we don't know which groups + # were updated during edition of groups and the user may no longer + # be part of the updated group (case of group removal) + for group in Group.objects.all(): + if hasattr(group, "listright"): + synchronise_usergroup(users.models.ListRight, instance=group.listright) + user_ldap.save() + +@receiver(users.signals.remove, sender=users.models.User) +def remove_user(sender, **kwargs): + user = kwargs["instance"] + try: + user_ldap = LdapUser.objects.get(name=user.pseudo) + user_ldap.delete() + except LdapUser.DoesNotExist: + pass + +@receiver(users.signals.remove_mass, sender=users.models.User) +def remove_users(sender, **kwargs): + queryset_users = kwargs["queryset"] + LdapUser.objects.filter( + name__in=list(queryset_users.values_list("pseudo", flat=True)) + ).delete() + + +class LdapUserGroup(ldapdb.models.Model): + """A class representing a LdapUserGroup in LDAP, its LDAP conterpart. + Synced from UserGroup, (ListRight/Group django models), + with a copy of its attributes/fields into LDAP, so this class is a mirror + of the classic django ListRight model. + + The basedn usergroupdn is specified in settings. + + Attributes: + name: The name of this LdapUserGroup + gid: The gid number for this unix group + members: Users dn members of this LdapUserGroup + """ + + # LDAP meta-data + base_dn = settings.LDAP["base_usergroup_dn"] + object_classes = ["posixGroup"] + + # attributes + gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") + members = ldapdb.models.fields.ListField(db_column="memberUid", blank=True) + name = ldapdb.models.fields.CharField( + db_column="cn", max_length=200, primary_key=True + ) + + def __str__(self): + return self.name + +@receiver(users.signals.synchronise, sender=users.models.ListRight) +def synchronise_usergroup(sender, **kwargs): + group = kwargs["instance"] + try: + group_ldap = LdapUserGroup.objects.get(gid=group.gid) + except LdapUserGroup.DoesNotExist: + group_ldap = LdapUserGroup(gid=group.gid) + group_ldap.name = group.unix_name + group_ldap.members = [user.pseudo for user in group.user_set.all()] + group_ldap.save() + +@receiver(users.signals.remove, sender=users.models.ListRight) +def remove_usergroup(sender, **kwargs): + group = kwargs["instance"] + try: + group_ldap = LdapUserGroup.objects.get(gid=group.gid) + group_ldap.delete() + except LdapUserGroup.DoesNotExist: + pass + + + +class LdapServiceUser(ldapdb.models.Model): + """A class representing a ServiceUser in LDAP, its LDAP conterpart. + Synced from ServiceUser, with a copy of its attributes/fields into LDAP, + so this class is a mirror of the classic django ServiceUser model. + + The basedn userservicedn is specified in settings. + + Attributes: + name: The name of this ServiceUser + user_password: The SSHA hashed password of this ServiceUser + """ + + # LDAP meta-data + base_dn = settings.LDAP["base_userservice_dn"] + object_classes = ["applicationProcess", "simpleSecurityObject"] + + # attributes + name = ldapdb.models.fields.CharField( + db_column="cn", max_length=200, primary_key=True + ) + user_password = ldapdb.models.fields.CharField( + db_column="userPassword", max_length=200, blank=True, null=True + ) + + def __str__(self): + return self.name + + +def synchronise_serviceuser_group(serviceuser): + try: + group = LdapServiceUserGroup.objects.get(name=serviceuser.access_group) + except: + group = LdapServiceUserGroup(name=serviceuser.access_group) + group.members = list( + LdapServiceUser.objects.filter( + name__in=[ + user.pseudo + for user in users.models.ServiceUser.objects.filter( + access_group=serviceuser.access_group + ) + ] + ).values_list("dn", flat=True) + ) + group.save() + + +@receiver(users.signals.synchronise, sender=users.models.ServiceUser) +def synchronise_serviceuser(sender, **kwargs): + user = kwargs["instance"] + try: + user_ldap = LdapServiceUser.objects.get(name=user.pseudo) + except LdapServiceUser.DoesNotExist: + user_ldap = LdapServiceUser(name=user.pseudo) + user_ldap.user_password = user.password[:6] + user.password[7:] + user_ldap.save() + synchronise_serviceuser_group(user) + +@receiver(users.signals.remove, sender=users.models.ServiceUser) +def remove_serviceuser(sender, **kwargs): + user = kwargs["instance"] + try: + user_ldap = LdapServiceUser.objects.get(name=user.pseudo) + user_ldap.delete() + except LdapUser.DoesNotExist: + pass + synchronise_serviceuser_group(user) + + +class LdapServiceUserGroup(ldapdb.models.Model): + """A class representing a ServiceUserGroup in LDAP, its LDAP conterpart. + Synced from ServiceUserGroup, with a copy of its attributes/fields into LDAP, + so this class is a mirror of the classic django ServiceUserGroup model. + + The basedn userservicegroupdn is specified in settings. + + Attributes: + name: The name of this ServiceUserGroup + members: ServiceUsers dn members of this ServiceUserGroup + """ + + # LDAP meta-data + base_dn = settings.LDAP["base_userservicegroup_dn"] + object_classes = ["groupOfNames"] + + # attributes + name = ldapdb.models.fields.CharField( + db_column="cn", max_length=200, primary_key=True + ) + members = ldapdb.models.fields.ListField(db_column="member", blank=True) + + def __str__(self): + return self.name + diff --git a/ldap_sync/tests.py b/ldap_sync/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/ldap_sync/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/ldap_sync/urls.py b/ldap_sync/urls.py new file mode 100644 index 00000000..24f25ce8 --- /dev/null +++ b/ldap_sync/urls.py @@ -0,0 +1,4 @@ +from django.conf.urls import url +from .import views + +urlpatterns = [] diff --git a/ldap_sync/views.py b/ldap_sync/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/ldap_sync/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/re2o/context_processors.py b/re2o/context_processors.py index c32c263f..07ee1c68 100644 --- a/re2o/context_processors.py +++ b/re2o/context_processors.py @@ -79,12 +79,12 @@ def context_optionnal_apps(request): optionnal_templates_navbar_user_list = [ app.views.navbar_user() for app in optionnal_apps - if hasattr(app.views, "navbar_user") + if hasattr(app, "views") and hasattr(app.views, "navbar_user") ] optionnal_templates_navbar_logout_list = [ app.views.navbar_logout() for app in optionnal_apps - if hasattr(app.views, "navbar_logout") + if hasattr(app, "views") and hasattr(app.views, "navbar_logout") ] return { "optionnal_templates_navbar_user_list": optionnal_templates_navbar_user_list, diff --git a/users/admin.py b/users/admin.py index a18dae90..d083a951 100644 --- a/users/admin.py +++ b/users/admin.py @@ -46,10 +46,6 @@ from .models import ( Ban, Whitelist, Request, - LdapUser, - LdapServiceUser, - LdapServiceUserGroup, - LdapUserGroup, ) from .forms import ( UserAdminForm, @@ -57,57 +53,6 @@ from .forms import ( ) -class LdapUserAdmin(admin.ModelAdmin): - """LdapUser Admin view. Can't change password, manage - by User General model. - - Parameters: - Django ModelAdmin: Apply on django ModelAdmin - - """ - list_display = ("name", "uidNumber", "login_shell") - exclude = ("user_password", "sambat_nt_password") - search_fields = ("name",) - - -class LdapServiceUserAdmin(admin.ModelAdmin): - """LdapServiceUser Admin view. Can't change password, manage - by User General model. - - Parameters: - Django ModelAdmin: Apply on django ModelAdmin - - """ - - list_display = ("name",) - exclude = ("user_password",) - search_fields = ("name",) - - -class LdapUserGroupAdmin(admin.ModelAdmin): - """LdapUserGroup Admin view. - - Parameters: - Django ModelAdmin: Apply on django ModelAdmin - - """ - - list_display = ("name", "members", "gid") - search_fields = ("name",) - - -class LdapServiceUserGroupAdmin(admin.ModelAdmin): - """LdapServiceUserGroup Admin view. - - Parameters: - Django ModelAdmin: Apply on django ModelAdmin - - """ - - list_display = ("name",) - search_fields = ("name",) - - class SchoolAdmin(VersionAdmin): """School Admin view and management. @@ -338,10 +283,6 @@ class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): admin.site.register(Adherent, AdherentAdmin) admin.site.register(Club, ClubAdmin) admin.site.register(ServiceUser, ServiceUserAdmin) -admin.site.register(LdapUser, LdapUserAdmin) -admin.site.register(LdapUserGroup, LdapUserGroupAdmin) -admin.site.register(LdapServiceUser, LdapServiceUserAdmin) -admin.site.register(LdapServiceUserGroup, LdapServiceUserGroupAdmin) admin.site.register(School, SchoolAdmin) admin.site.register(ListRight, ListRightAdmin) admin.site.register(ListShell, ListShellAdmin) diff --git a/users/migrations/0004_auto_20210110_1811.py b/users/migrations/0004_auto_20210110_1811.py new file mode 100644 index 00000000..f6179f01 --- /dev/null +++ b/users/migrations/0004_auto_20210110_1811.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2021-01-10 17:11 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup'), + ] + + operations = [ + migrations.DeleteModel( + name='LdapServiceUser', + ), + migrations.DeleteModel( + name='LdapServiceUserGroup', + ), + migrations.DeleteModel( + name='LdapUser', + ), + migrations.DeleteModel( + name='LdapUserGroup', + ), + ] diff --git a/users/models.py b/users/models.py index 2b6eaacf..8313bfd7 100755 --- a/users/models.py +++ b/users/models.py @@ -39,14 +39,6 @@ Here are defined the following django models : * Schools (teaching structures) * Rights (Groups and ListRight) * ServiceUser (for ldap connexions) - -Also define django-ldapdb models : - * LdapUser - * LdapGroup - * LdapServiceUser - -These objects are sync from django regular models as auxiliary models from -sql data into ldap. """ @@ -82,8 +74,6 @@ from django.core.files.uploadedfile import InMemoryUploadedFile from reversion import revisions as reversion -import ldapdb.models -import ldapdb.models.fields from re2o.settings import LDAP, GID_RANGES, UID_RANGES from re2o.field_permissions import FieldPermissionModelMixin @@ -96,6 +86,8 @@ from machines.models import Domain, Interface, Machine, regen from preferences.models import GeneralOption, AssoOption, OptionalUser from preferences.models import OptionalMachine, MailMessageOption +from users import signals + from PIL import Image from io import BytesIO import sys @@ -1042,7 +1034,7 @@ class User( """ cls.mass_disable_email(queryset_users) Machine.mass_delete(Machine.objects.filter(user__in=queryset_users)) - cls.ldap_delete_users(queryset_users) + signals.remove_mass.send(sender=cls, queryset=queryset_users) def archive(self): """Method, archive user by unassigning ips. @@ -1072,7 +1064,7 @@ class User( def full_archive(self): """Method, full archive an user by unassigning ips, deleting data - and ldap deletion. + and authentication deletion. Parameters: self (user instance): user to full archive. @@ -1080,7 +1072,7 @@ class User( """ self.archive() self.delete_data() - self.ldap_del() + signals.remove.send(sender=User, instance=self) @classmethod def mass_full_archive(cls, users_list): @@ -1102,14 +1094,14 @@ class User( def unarchive(self): """Method, unarchive an user by assigning ips, and recreating - ldap user associated. + authentication user associated. Parameters: self (user instance): user to unarchive. """ self.assign_ips() - self.ldap_sync() + signals.synchronise.send(sender=self.__class__, instance=self) def state_sync(self): """Master Method, call unarchive, full_archive or archive method @@ -1135,109 +1127,6 @@ class User( ): self.full_archive() - def ldap_sync( - self, base=True, access_refresh=True, mac_refresh=True, group_refresh=False - ): - """Method ldap_sync, sync in ldap with self user attributes. - Each User instance is copy into ldap, via a LdapUser virtual objects. - This method performs a copy of several attributes (name, surname, mail, - hashed SSHA password, ntlm password, shell, homedirectory). - - Update, or create if needed a ldap entry related with the User instance. - - Parameters: - self (user instance): user to sync in ldap. - base (boolean): Default true, if base is true, perform a basic - sync of basic attributes. - access_refresh (boolean): Default true, if access_refresh is true, - update the dialup_access attributes based on has_access (is this user - has a valid internet access). - mac_refresh (boolean): Default true, if mac_refresh, update the mac_address - list of the user. - group_refresh (boolean): Default False, if true, update the groups membership - of this user. Onerous option, call ldap_sync() on every groups of the user. - - """ - if sys.version_info[0] >= 3 and ( - self.state == self.STATE_ACTIVE - or self.state == self.STATE_ARCHIVE - or self.state == self.STATE_DISABLED - ): - self.refresh_from_db() - try: - user_ldap = LdapUser.objects.get(uidNumber=self.uid_number) - except LdapUser.DoesNotExist: - user_ldap = LdapUser(uidNumber=self.uid_number) - base = True - access_refresh = True - mac_refresh = True - if base: - user_ldap.name = self.pseudo - user_ldap.sn = self.pseudo - user_ldap.dialupAccess = str(self.has_access()) - user_ldap.home_directory = self.home_directory - user_ldap.mail = self.get_mail - user_ldap.given_name = ( - self.surname.lower() + "_" + self.name.lower()[:3] - ) - user_ldap.gid = LDAP["user_gid"] - if "{SSHA}" in self.password or "{SMD5}" in self.password: - # We remove the extra $ added at import from ldap - user_ldap.user_password = self.password[:6] + self.password[7:] - elif "{crypt}" in self.password: - # depending on the length, we need to remove or not a $ - if len(self.password) == 41: - user_ldap.user_password = self.password - else: - user_ldap.user_password = self.password[:7] + self.password[8:] - - user_ldap.sambat_nt_password = self.pwd_ntlm.upper() - if self.get_shell: - user_ldap.login_shell = str(self.get_shell) - user_ldap.shadowexpire = self.get_shadow_expire - if access_refresh: - user_ldap.dialupAccess = str(self.has_access()) - if mac_refresh: - user_ldap.macs = [ - str(mac) - for mac in Interface.objects.filter(machine__user=self) - .values_list("mac_address", flat=True) - .distinct() - ] - if group_refresh: - # Need to refresh all groups because we don't know which groups - # were updated during edition of groups and the user may no longer - # be part of the updated group (case of group removal) - for group in Group.objects.all(): - if hasattr(group, "listright"): - group.listright.ldap_sync() - user_ldap.save() - - def ldap_del(self): - """Method, delete an user in ldap. - - Parameters: - self (user instance): user to delete in Ldap. - - """ - try: - user_ldap = LdapUser.objects.get(name=self.pseudo) - user_ldap.delete() - except LdapUser.DoesNotExist: - pass - - @classmethod - def ldap_delete_users(cls, queryset_users): - """Class method, delete several users in ldap (queryset). - - Parameters: - queryset_users (list of users queryset): users to delete - in ldap. - """ - LdapUser.objects.filter( - name__in=list(queryset_users.values_list("pseudo", flat=True)) - ) - ###### Send mail functions ###### def notif_inscription(self, request=None): @@ -2195,7 +2084,7 @@ class Club(User): @receiver(post_save, sender=User) def user_post_save(**kwargs): """Django signal, post save operations on Adherent, Club and User. - Sync pseudo, sync ldap, create mailalias and send welcome email if needed + Sync pseudo, sync authentication, create mailalias and send welcome email if needed (new user) """ @@ -2207,8 +2096,7 @@ def user_post_save(**kwargs): user.notif_inscription(user.request) user.set_active() user.state_sync() - user.ldap_sync( - base=True, access_refresh=True, mac_refresh=False, group_refresh=True + signals.synchronise.send(sender=User, instance=user, base=True, access_refresh=True, mac_refresh=False, group_refresh=True ) regen("mailing") @@ -2216,14 +2104,13 @@ def user_post_save(**kwargs): @receiver(m2m_changed, sender=User.groups.through) def user_group_relation_changed(**kwargs): """Django signal, used for User Groups change (related models). - Sync ldap, with calling group_refresh. + Sync authentication, with calling group_refresh. """ action = kwargs["action"] if action in ("post_add", "post_remove", "post_clear"): user = kwargs["instance"] - user.ldap_sync( - base=False, access_refresh=False, mac_refresh=False, group_refresh=True + signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=False, mac_refresh=False, group_refresh=True ) @@ -2232,20 +2119,20 @@ def user_group_relation_changed(**kwargs): @receiver(post_delete, sender=User) def user_post_delete(**kwargs): """Django signal, post delete operations on Adherent, Club and User. - Delete user in ldap. + Delete user in authentication. """ user = kwargs["instance"] - user.ldap_del() + signals.remove.send(sender=User, instance=user) regen("mailing") class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): """A class representing a serviceuser (it is considered as a user with special informations). - The serviceuser is a special user used with special access to ldap tree. It is + The serviceuser is a special user used with special access to authentication tree. It is its only usefullness, and service user can't connect to re2o. - Each service connected to ldap for auth (ex dokuwiki, owncloud, etc) should + Each service connected to authentication for auth (ex dokuwiki, owncloud, etc) should have a different service user with special acl (readonly, auth) and password. Attributes: @@ -2293,65 +2180,6 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): """ return self.pseudo - def ldap_sync(self): - """Method ldap_sync, sync the serviceuser in ldap with its attributes. - Each ServiceUser instance is copy into ldap, via a LdapServiceUser virtual object. - This method performs a copy of several attributes (pseudo, access). - - Update, or create if needed a mirror ldap entry related with the ServiceUserinstance. - - Parameters: - self (serviceuser instance): ServiceUser to sync in ldap. - - """ - try: - user_ldap = LdapServiceUser.objects.get(name=self.pseudo) - except LdapServiceUser.DoesNotExist: - user_ldap = LdapServiceUser(name=self.pseudo) - user_ldap.user_password = self.password[:6] + self.password[7:] - user_ldap.save() - self.serviceuser_group_sync() - - def ldap_del(self): - """Method, delete an ServiceUser in ldap. - - Parameters: - self (ServiceUser instance): serviceuser to delete in Ldap. - - """ - try: - user_ldap = LdapServiceUser.objects.get(name=self.pseudo) - user_ldap.delete() - except LdapUser.DoesNotExist: - pass - self.serviceuser_group_sync() - - def serviceuser_group_sync(self): - """Method, update serviceuser group sync in ldap. - In LDAP, Acl depends on the ldapgroup (readonly, auth, or usermgt), - so the ldap group need to be synced with the accessgroup field on ServiceUser. - Called by ldap_sync and ldap_del. - - Parameters: - self (ServiceUser instance): serviceuser to update groups in LDAP. - - """ - try: - group = LdapServiceUserGroup.objects.get(name=self.access_group) - except: - group = LdapServiceUserGroup(name=self.access_group) - group.members = list( - LdapServiceUser.objects.filter( - name__in=[ - user.pseudo - for user in ServiceUser.objects.filter( - access_group=self.access_group - ) - ] - ).values_list("dn", flat=True) - ) - group.save() - def __str__(self): return self.pseudo @@ -2359,21 +2187,21 @@ class ServiceUser(RevMixin, AclMixin, AbstractBaseUser): @receiver(post_save, sender=ServiceUser) def service_user_post_save(**kwargs): """Django signal, post save operations on ServiceUser. - Sync or create serviceuser in ldap. + Sync or create serviceuser in authentication. """ service_user = kwargs["instance"] - service_user.ldap_sync() + signals.synchronise.send(sender=ServiceUser, instance=service_user) @receiver(post_delete, sender=ServiceUser) def service_user_post_delete(**kwargs): """Django signal, post delete operations on ServiceUser. - Delete service user in ldap. + Delete service user in authentication. """ service_user = kwargs["instance"] - service_user.ldap_del() + signals.remove.send(sender=ServiceUser, instance=service_user) class School(RevMixin, AclMixin, models.Model): @@ -2448,58 +2276,25 @@ class ListRight(RevMixin, AclMixin, Group): def __str__(self): return self.name - def ldap_sync(self): - """Method ldap_sync, sync the listright/group in ldap with its listright attributes. - Each ListRight/Group instance is copy into ldap, via a LdapUserGroup virtual objects. - This method performs a copy of several attributes (name, members, gid, etc). - The primary key is the gid, and should never change. - - Update, or create if needed a ldap entry related with the ListRight/Group instance. - - Parameters: - self (listright instance): ListRight/Group to sync in ldap. - - """ - try: - group_ldap = LdapUserGroup.objects.get(gid=self.gid) - except LdapUserGroup.DoesNotExist: - group_ldap = LdapUserGroup(gid=self.gid) - group_ldap.name = self.unix_name - group_ldap.members = [user.pseudo for user in self.user_set.all()] - group_ldap.save() - - def ldap_del(self): - """Method, delete an ListRight/Group in ldap. - - Parameters: - self (listright/Group instance): group to delete in Ldap. - - """ - try: - group_ldap = LdapUserGroup.objects.get(gid=self.gid) - group_ldap.delete() - except LdapUserGroup.DoesNotExist: - pass - @receiver(post_save, sender=ListRight) def listright_post_save(**kwargs): """Django signal, post save operations on ListRight/Group objects. - Sync or create group in ldap. + Sync or create group in authentication. """ right = kwargs["instance"] - right.ldap_sync() + signals.synchronise.send(sender=ListRight, instance=right) @receiver(post_delete, sender=ListRight) def listright_post_delete(**kwargs): """Django signal, post delete operations on ListRight/Group objects. - Delete group in ldap. + Delete group in authentication. """ right = kwargs["instance"] - right.ldap_del() + signals.remove.send(sender=ListRight, instance=right) class ListShell(RevMixin, AclMixin, models.Model): @@ -2649,13 +2444,13 @@ class Ban(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Ban) def ban_post_save(**kwargs): """Django signal, post save operations on Ban objects. - Sync user's access state in ldap, call email notification if needed. + Sync user's access state in authentication, call email notification if needed. """ ban = kwargs["instance"] is_created = kwargs["created"] user = ban.user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False) regen("mailing") if is_created: ban.notif_ban(ban.request) @@ -2669,11 +2464,11 @@ def ban_post_save(**kwargs): @receiver(post_delete, sender=Ban) def ban_post_delete(**kwargs): """Django signal, post delete operations on Ban objects. - Sync user's access state in ldap. + Sync user's access state in authentication. """ user = kwargs["instance"].user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False) regen("mailing") regen("dhcp") regen("mac_ip_list") @@ -2740,12 +2535,12 @@ class Whitelist(RevMixin, AclMixin, models.Model): @receiver(post_save, sender=Whitelist) def whitelist_post_save(**kwargs): """Django signal, post save operations on Whitelist objects. - Sync user's access state in ldap. + Sync user's access state in authentication. """ whitelist = kwargs["instance"] user = whitelist.user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False) is_created = kwargs["created"] regen("mailing") if is_created: @@ -2759,11 +2554,11 @@ def whitelist_post_save(**kwargs): @receiver(post_delete, sender=Whitelist) def whitelist_post_delete(**kwargs): """Django signal, post delete operations on Whitelist objects. - Sync user's access state in ldap. + Sync user's access state in authentication. """ user = kwargs["instance"].user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + signals.synchronise.send(sender=User, instance=user, base=False, access_refresh=True, mac_refresh=False) regen("mailing") regen("dhcp") regen("mac_ip_list") @@ -2996,171 +2791,3 @@ class EMailAddress(RevMixin, AclMixin, models.Model): raise ValidationError(reason) super(EMailAddress, self).clean(*args, **kwargs) - -class LdapUser(ldapdb.models.Model): - """A class representing a LdapUser in LDAP, its LDAP conterpart. - Synced from re2o django User model, (User django models), - with a copy of its attributes/fields into LDAP, so this class is a mirror - of the classic django User model. - - The basedn userdn is specified in settings. - - Attributes: - name: The name of this User - uid: The uid (login) for the unix user - uidNumber: Linux uid number - gid: The default gid number for this user - sn: The user "str" pseudo - login_shell: Linux shell for the user - mail: Email address contact for this user - display_name: Pretty display name for this user - dialupAccess: Boolean, True for valid membership - sambaSID: Identical id as uidNumber - user_password: SSHA hashed password of user - samba_nt_password: NTLM hashed password of user - macs: Multivalued mac address - shadowexpire: Set it to 0 to block access for this user and disabled - account - """ - - # LDAP meta-data - base_dn = LDAP["base_user_dn"] - object_classes = [ - "inetOrgPerson", - "top", - "posixAccount", - "sambaSamAccount", - "radiusprofile", - "shadowAccount", - ] - - # attributes - gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") - name = ldapdb.models.fields.CharField( - db_column="cn", max_length=200, primary_key=True - ) - uid = ldapdb.models.fields.CharField(db_column="uid", max_length=200) - uidNumber = ldapdb.models.fields.IntegerField(db_column="uidNumber", unique=True) - sn = ldapdb.models.fields.CharField(db_column="sn", max_length=200) - login_shell = ldapdb.models.fields.CharField( - db_column="loginShell", max_length=200, blank=True, null=True - ) - mail = ldapdb.models.fields.CharField(db_column="mail", max_length=200) - given_name = ldapdb.models.fields.CharField(db_column="givenName", max_length=200) - home_directory = ldapdb.models.fields.CharField( - db_column="homeDirectory", max_length=200 - ) - display_name = ldapdb.models.fields.CharField( - db_column="displayName", max_length=200, blank=True, null=True - ) - dialupAccess = ldapdb.models.fields.CharField(db_column="dialupAccess") - sambaSID = ldapdb.models.fields.IntegerField(db_column="sambaSID", unique=True) - user_password = ldapdb.models.fields.CharField( - db_column="userPassword", max_length=200, blank=True, null=True - ) - sambat_nt_password = ldapdb.models.fields.CharField( - db_column="sambaNTPassword", max_length=200, blank=True, null=True - ) - macs = ldapdb.models.fields.ListField( - db_column="radiusCallingStationId", max_length=200, blank=True, null=True - ) - shadowexpire = ldapdb.models.fields.CharField( - db_column="shadowExpire", blank=True, null=True - ) - - def __str__(self): - return self.name - - def __unicode__(self): - return self.name - - def save(self, *args, **kwargs): - self.sn = self.name - self.uid = self.name - self.sambaSID = self.uidNumber - super(LdapUser, self).save(*args, **kwargs) - - -class LdapUserGroup(ldapdb.models.Model): - """A class representing a LdapUserGroup in LDAP, its LDAP conterpart. - Synced from UserGroup, (ListRight/Group django models), - with a copy of its attributes/fields into LDAP, so this class is a mirror - of the classic django ListRight model. - - The basedn usergroupdn is specified in settings. - - Attributes: - name: The name of this LdapUserGroup - gid: The gid number for this unix group - members: Users dn members of this LdapUserGroup - """ - - # LDAP meta-data - base_dn = LDAP["base_usergroup_dn"] - object_classes = ["posixGroup"] - - # attributes - gid = ldapdb.models.fields.IntegerField(db_column="gidNumber") - members = ldapdb.models.fields.ListField(db_column="memberUid", blank=True) - name = ldapdb.models.fields.CharField( - db_column="cn", max_length=200, primary_key=True - ) - - def __str__(self): - return self.name - - -class LdapServiceUser(ldapdb.models.Model): - """A class representing a ServiceUser in LDAP, its LDAP conterpart. - Synced from ServiceUser, with a copy of its attributes/fields into LDAP, - so this class is a mirror of the classic django ServiceUser model. - - The basedn userservicedn is specified in settings. - - Attributes: - name: The name of this ServiceUser - user_password: The SSHA hashed password of this ServiceUser - """ - - # LDAP meta-data - base_dn = LDAP["base_userservice_dn"] - object_classes = ["applicationProcess", "simpleSecurityObject"] - - # attributes - name = ldapdb.models.fields.CharField( - db_column="cn", max_length=200, primary_key=True - ) - user_password = ldapdb.models.fields.CharField( - db_column="userPassword", max_length=200, blank=True, null=True - ) - - def __str__(self): - return self.name - - -class LdapServiceUserGroup(ldapdb.models.Model): - """A class representing a ServiceUserGroup in LDAP, its LDAP conterpart. - Synced from ServiceUserGroup, with a copy of its attributes/fields into LDAP, - so this class is a mirror of the classic django ServiceUserGroup model. - - The basedn userservicegroupdn is specified in settings. - - Attributes: - name: The name of this ServiceUserGroup - members: ServiceUsers dn members of this ServiceUserGroup - """ - - # LDAP meta-data - base_dn = LDAP["base_userservicegroup_dn"] - object_classes = ["groupOfNames"] - - # attributes - name = ldapdb.models.fields.CharField( - db_column="cn", max_length=200, primary_key=True - ) - members = ldapdb.models.fields.ListField(db_column="member", blank=True) - - def __str__(self): - return self.name - - diff --git a/users/signals.py b/users/signals.py new file mode 100644 index 00000000..7336e5fc --- /dev/null +++ b/users/signals.py @@ -0,0 +1,33 @@ +""" +A set of signals used by users. Various classes in users emit these signals to signal the need to sync or +remove an object from optionnal authentication backends, e.g. LDAP. + +* `users.signals.synchronise`: + Expresses the need for an instance of a users class to be synchronised. `sender` and `instance` are + always set. It is up to the receiver to ensure the others are set correctly if they make sense. + Arguments: + * `sender` : The model class. + * `instance` : The actual instance being synchronised. + * `base` : Default `True`. When `True`, synchronise basic attributes. + * `access_refresh` : Default `True`. When `True`, synchronise the access time. + * `mac_refresh` : Default `True`. When True, synchronise the list of mac addresses. + * `group_refresh`: Default `False`. When `True` synchronise the groups of the instance. +* `users.signals.remove`: + Expresses the need for an instance of a users class to be removed. + Arguments: + * `sender` : The model class. + * `instance` : The actual instance being removed. +* `users.signals.remove_mass`: + Same as `users.signals.remove` except it removes a queryset. For now it is only used by `users.models.User`. + Arguments: + * `sender` : The model class. + * `queryset` : The actual instances being removed. + +""" + +import django.dispatch + +synchronise = django.dispatch.Signal(providing_args=["sender", "instance", "base", "access_refresh", "mac_refresh", "group_refresh"]) +remove = django.dispatch.Signal(providing_args=["sender", "instance"]) +remove_mass = django.dispatch.Signal(providing_args=["sender", "queryset"]) + From 4a3d0f346b2210f8addcfbf72854fc69ddc03ed9 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 10 Jan 2021 19:35:24 +0100 Subject: [PATCH 470/490] feat: Integrate the removal of LDAP to migration squash. --- cotisations/migrations/0001_model_creation.py | 1 + cotisations/migrations/0002_foreign_keys.py | 1 + ldap_sync/migrations/0001_initial.py | 2 +- machines/migrations/0001_model_creation.py | 1 + machines/migrations/0002_foreign_keys.py | 1 + preferences/migrations/0001_model_creation.py | 1 + preferences/migrations/0002_foreign_keys.py | 1 + topologie/migrations/0001_model_creation.py | 1 + topologie/migrations/0002_foreign_keys.py | 1 + users/migrations/0001_model_creation.py | 1 + users/migrations/0002_foreign_keys.py | 1 + ...serviceusergroup_ldapuser_ldapusergroup.py | 514 ------------------ ...110_1811.py => 0096_auto_20210110_1811.py} | 2 +- 13 files changed, 12 insertions(+), 516 deletions(-) delete mode 100644 users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py rename users/migrations/{0004_auto_20210110_1811.py => 0096_auto_20210110_1811.py} (86%) diff --git a/cotisations/migrations/0001_model_creation.py b/cotisations/migrations/0001_model_creation.py index aa45127a..fcd4e769 100644 --- a/cotisations/migrations/0001_model_creation.py +++ b/cotisations/migrations/0001_model_creation.py @@ -112,6 +112,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/cotisations/migrations/0002_foreign_keys.py b/cotisations/migrations/0002_foreign_keys.py index 0828aa5d..73353baf 100644 --- a/cotisations/migrations/0002_foreign_keys.py +++ b/cotisations/migrations/0002_foreign_keys.py @@ -111,6 +111,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/ldap_sync/migrations/0001_initial.py b/ldap_sync/migrations/0001_initial.py index 5a4c448e..01985ee8 100644 --- a/ldap_sync/migrations/0001_initial.py +++ b/ldap_sync/migrations/0001_initial.py @@ -22,7 +22,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('users', '0004_auto_20210110_1811') + ('users', '0002_foreign_keys') ] operations = [ diff --git a/machines/migrations/0001_model_creation.py b/machines/migrations/0001_model_creation.py index a16505a9..96c8dffc 100644 --- a/machines/migrations/0001_model_creation.py +++ b/machines/migrations/0001_model_creation.py @@ -111,6 +111,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/machines/migrations/0002_foreign_keys.py b/machines/migrations/0002_foreign_keys.py index 4d864b22..3ace9b2e 100644 --- a/machines/migrations/0002_foreign_keys.py +++ b/machines/migrations/0002_foreign_keys.py @@ -111,6 +111,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/preferences/migrations/0001_model_creation.py b/preferences/migrations/0001_model_creation.py index b3ece734..ff399741 100644 --- a/preferences/migrations/0001_model_creation.py +++ b/preferences/migrations/0001_model_creation.py @@ -106,6 +106,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/preferences/migrations/0002_foreign_keys.py b/preferences/migrations/0002_foreign_keys.py index 262b64bb..e1890776 100644 --- a/preferences/migrations/0002_foreign_keys.py +++ b/preferences/migrations/0002_foreign_keys.py @@ -116,6 +116,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/topologie/migrations/0001_model_creation.py b/topologie/migrations/0001_model_creation.py index 76b9992e..1367271e 100644 --- a/topologie/migrations/0001_model_creation.py +++ b/topologie/migrations/0001_model_creation.py @@ -111,6 +111,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/topologie/migrations/0002_foreign_keys.py b/topologie/migrations/0002_foreign_keys.py index 492c97f6..9b2eb304 100644 --- a/topologie/migrations/0002_foreign_keys.py +++ b/topologie/migrations/0002_foreign_keys.py @@ -112,6 +112,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/users/migrations/0001_model_creation.py b/users/migrations/0001_model_creation.py index 3cefd817..6cdfd2e7 100644 --- a/users/migrations/0001_model_creation.py +++ b/users/migrations/0001_model_creation.py @@ -113,6 +113,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/users/migrations/0002_foreign_keys.py b/users/migrations/0002_foreign_keys.py index 7c471927..55358f03 100644 --- a/users/migrations/0002_foreign_keys.py +++ b/users/migrations/0002_foreign_keys.py @@ -114,6 +114,7 @@ class Migration(migrations.Migration): ("users", "0093_user_profile_image"), ("users", "0094_remove_user_profile_image"), ("users", "0095_user_theme"), + ("users", "0096_auto_20210110_1811"), ("cotisations", "0001_initial"), ("cotisations", "0002_remove_facture_article"), ("cotisations", "0003_auto_20160702_1448"), diff --git a/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py b/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py deleted file mode 100644 index 8a960211..00000000 --- a/users/migrations/0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup.py +++ /dev/null @@ -1,514 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.29 on 2020-12-30 17:47 -from __future__ import unicode_literals - -from django.db import migrations -import ldapdb.models.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0002_foreign_keys'), - ] - - replaces = [ - ("users", "0001_initial"), - ("users", "0002_auto_20160630_2301"), - ("users", "0003_listrights_rights"), - ("users", "0004_auto_20160701_2312"), - ("users", "0005_auto_20160702_0006"), - ("users", "0006_ban"), - ("users", "0007_auto_20160702_2322"), - ("users", "0008_user_registered"), - ("users", "0009_user_room"), - ("users", "0010_auto_20160703_1226"), - ("users", "0011_auto_20160703_1227"), - ("users", "0012_auto_20160703_1230"), - ("users", "0013_auto_20160704_1547"), - ("users", "0014_auto_20160704_1548"), - ("users", "0015_whitelist"), - ("users", "0016_auto_20160706_1220"), - ("users", "0017_auto_20160707_0105"), - ("users", "0018_auto_20160707_0115"), - ("users", "0019_auto_20160708_1633"), - ("users", "0020_request"), - ("users", "0021_ldapuser"), - ("users", "0022_ldapuser_sambasid"), - ("users", "0023_auto_20160724_1908"), - ("users", "0024_remove_ldapuser_mac_list"), - ("users", "0025_listshell"), - ("users", "0026_user_shell"), - ("users", "0027_auto_20160726_0216"), - ("users", "0028_auto_20160726_0227"), - ("users", "0029_auto_20160726_0229"), - ("users", "0030_auto_20160726_0357"), - ("users", "0031_auto_20160726_0359"), - ("users", "0032_auto_20160727_2122"), - ("users", "0033_remove_ldapuser_loginshell"), - ("users", "0034_auto_20161018_0037"), - ("users", "0035_auto_20161018_0046"), - ("users", "0036_auto_20161022_2146"), - ("users", "0037_auto_20161028_1906"), - ("users", "0038_auto_20161031_0258"), - ("users", "0039_auto_20161119_0033"), - ("users", "0040_auto_20161119_1709"), - ("users", "0041_listright_details"), - ("users", "0042_auto_20161126_2028"), - ("users", "0043_auto_20161224_1156"), - ("users", "0043_ban_state"), - ("users", "0044_user_ssh_public_key"), - ("users", "0045_merge"), - ("users", "0046_auto_20170617_1433"), - ("users", "0047_auto_20170618_0156"), - ("users", "0048_auto_20170618_0210"), - ("users", "0049_auto_20170618_1424"), - ("users", "0050_serviceuser_comment"), - ("users", "0051_user_telephone"), - ("users", "0052_ldapuser_shadowexpire"), - ("users", "0053_auto_20170626_2105"), - ("users", "0054_auto_20170626_2219"), - ("users", "0055_auto_20171003_0556"), - ("users", "0056_auto_20171015_2033"), - ("users", "0057_auto_20171023_0301"), - ("users", "0058_auto_20171025_0154"), - ("users", "0059_auto_20171025_1854"), - ("users", "0060_auto_20171120_0317"), - ("users", "0061_auto_20171230_2033"), - ("users", "0062_auto_20171231_0056"), - ("users", "0063_auto_20171231_0140"), - ("users", "0064_auto_20171231_0150"), - ("users", "0065_auto_20171231_2053"), - ("users", "0066_grouppermissions"), - ("users", "0067_serveurpermission"), - ("users", "0068_auto_20180107_2245"), - ("users", "0069_club_mailing"), - ("users", "0070_auto_20180324_1906"), - ("users", "0071_auto_20180415_1252"), - ("users", "0072_auto_20180426_2021"), - ("users", "0073_auto_20180629_1614"), - ("users", "0074_auto_20180810_2104"), - ("users", "0074_auto_20180814_1059"), - ("users", "0075_merge_20180815_2202"), - ("users", "0076_auto_20180818_1321"), - ("users", "0077_auto_20180824_1750"), - ("users", "0078_auto_20181011_1405"), - ("users", "0079_auto_20181228_2039"), - ("users", "0080_auto_20190108_1726"), - ("users", "0081_auto_20190317_0302"), - ("users", "0082_auto_20190908_1338"), - ("users", "0083_user_shortcuts_enabled"), - ("users", "0084_auto_20191120_0159"), - ("users", "0085_user_email_state"), - ("users", "0086_user_email_change_date"), - ("users", "0087_request_email"), - ("users", "0088_auto_20200417_2312"), - ("users", "0089_auto_20200418_0112"), - ("users", "0090_auto_20200421_1825"), - ("users", "0091_auto_20200423_1256"), - ("users", "0092_auto_20200502_0057"), - ("users", "0093_user_profile_image"), - ("users", "0094_remove_user_profile_image"), - ("users", "0095_user_theme"), - ("cotisations", "0001_initial"), - ("cotisations", "0002_remove_facture_article"), - ("cotisations", "0003_auto_20160702_1448"), - ("cotisations", "0004_auto_20160702_1528"), - ("cotisations", "0005_auto_20160702_1532"), - ("cotisations", "0006_auto_20160702_1534"), - ("cotisations", "0007_auto_20160702_1543"), - ("cotisations", "0008_auto_20160702_1614"), - ("cotisations", "0009_remove_cotisation_user"), - ("cotisations", "0010_auto_20160702_1840"), - ("cotisations", "0011_auto_20160702_1911"), - ("cotisations", "0012_auto_20160704_0118"), - ("cotisations", "0013_auto_20160711_2240"), - ("cotisations", "0014_auto_20160712_0245"), - ("cotisations", "0015_auto_20160714_2142"), - ("cotisations", "0016_auto_20160715_0110"), - ("cotisations", "0017_auto_20170718_2329"), - ("cotisations", "0018_paiement_type_paiement"), - ("cotisations", "0019_auto_20170819_0055"), - ("cotisations", "0020_auto_20170819_0057"), - ("cotisations", "0021_auto_20170819_0104"), - ("cotisations", "0022_auto_20170824_0128"), - ("cotisations", "0023_auto_20170902_1303"), - ("cotisations", "0024_auto_20171015_2033"), - ("cotisations", "0025_article_type_user"), - ("cotisations", "0026_auto_20171028_0126"), - ("cotisations", "0027_auto_20171029_1156"), - ("cotisations", "0028_auto_20171231_0007"), - ("cotisations", "0029_auto_20180414_2056"), - ("cotisations", "0030_custom_payment"), - ("cotisations", "0031_comnpaypayment_production"), - ("cotisations", "0032_custom_invoice"), - ("cotisations", "0033_auto_20180818_1319"), - ("cotisations", "0034_auto_20180831_1532"), - ("cotisations", "0035_notepayment"), - ("cotisations", "0036_custominvoice_remark"), - ("cotisations", "0037_costestimate"), - ("cotisations", "0038_auto_20181231_1657"), - ("cotisations", "0039_freepayment"), - ("cotisations", "0040_auto_20191002_2335"), - ("cotisations", "0041_auto_20191103_2131"), - ("cotisations", "0042_auto_20191120_0159"), - ("cotisations", "0043_separation_membership_connection_p1"), - ("cotisations", "0044_separation_membership_connection_p2"), - ("cotisations", "0045_separation_membership_connection_p3"), - ("cotisations", "0046_article_need_membership"), - ("cotisations", "0047_article_need_membership_init"), - ("cotisations", "0048_auto_20201017_0018"), - ("cotisations", "0049_auto_20201102_2305"), - ("cotisations", "0050_auto_20201102_2342"), - ("cotisations", "0051_auto_20201228_1636"), - ("machines", "0001_initial"), - ("machines", "0002_auto_20160703_1444"), - ("machines", "0003_auto_20160703_1450"), - ("machines", "0004_auto_20160703_1451"), - ("machines", "0005_auto_20160703_1523"), - ("machines", "0006_auto_20160703_1813"), - ("machines", "0007_auto_20160703_1816"), - ("machines", "0008_remove_interface_ipv6"), - ("machines", "0009_auto_20160703_2358"), - ("machines", "0010_auto_20160704_0104"), - ("machines", "0011_auto_20160704_0105"), - ("machines", "0012_auto_20160704_0118"), - ("machines", "0013_auto_20160705_1014"), - ("machines", "0014_auto_20160706_1220"), - ("machines", "0015_auto_20160707_0105"), - ("machines", "0016_auto_20160708_1633"), - ("machines", "0017_auto_20160708_1645"), - ("machines", "0018_auto_20160708_1813"), - ("machines", "0019_auto_20160718_1141"), - ("machines", "0020_auto_20160718_1849"), - ("machines", "0021_auto_20161006_1943"), - ("machines", "0022_auto_20161011_1829"), - ("machines", "0023_iplist_ip_type"), - ("machines", "0024_machinetype_need_infra"), - ("machines", "0025_auto_20161023_0038"), - ("machines", "0026_auto_20161026_1348"), - ("machines", "0027_alias"), - ("machines", "0028_iptype_domaine_ip"), - ("machines", "0029_iptype_domaine_range"), - ("machines", "0030_auto_20161118_1730"), - ("machines", "0031_auto_20161119_1709"), - ("machines", "0032_auto_20161119_1850"), - ("machines", "0033_extension_need_infra"), - ("machines", "0034_iplist_need_infra"), - ("machines", "0035_auto_20161224_1201"), - ("machines", "0036_auto_20161224_1204"), - ("machines", "0037_domain_cname"), - ("machines", "0038_auto_20161224_1721"), - ("machines", "0039_auto_20161224_1732"), - ("machines", "0040_remove_interface_dns"), - ("machines", "0041_remove_ns_interface"), - ("machines", "0042_ns_ns"), - ("machines", "0043_auto_20170721_0350"), - ("machines", "0044_auto_20170808_0233"), - ("machines", "0045_auto_20170808_0348"), - ("machines", "0046_auto_20170808_1423"), - ("machines", "0047_auto_20170809_0606"), - ("machines", "0048_auto_20170823_2315"), - ("machines", "0049_vlan"), - ("machines", "0050_auto_20170826_0022"), - ("machines", "0051_iptype_vlan"), - ("machines", "0052_auto_20170828_2322"), - ("machines", "0053_text"), - ("machines", "0054_text_zone"), - ("machines", "0055_nas"), - ("machines", "0056_nas_port_access_mode"), - ("machines", "0057_nas_autocapture_mac"), - ("machines", "0058_auto_20171002_0350"), - ("machines", "0059_iptype_prefix_v6"), - ("machines", "0060_iptype_ouverture_ports"), - ("machines", "0061_auto_20171015_2033"), - ("machines", "0062_extension_origin_v6"), - ("machines", "0063_auto_20171020_0040"), - ("machines", "0064_auto_20171115_0253"), - ("machines", "0065_auto_20171115_1514"), - ("machines", "0066_srv"), - ("machines", "0067_auto_20171116_0152"), - ("machines", "0068_auto_20171116_0252"), - ("machines", "0069_auto_20171116_0822"), - ("machines", "0070_auto_20171231_1947"), - ("machines", "0071_auto_20171231_2100"), - ("machines", "0072_auto_20180108_1822"), - ("machines", "0073_auto_20180128_2203"), - ("machines", "0074_auto_20180129_0352"), - ("machines", "0075_auto_20180130_0052"), - ("machines", "0076_auto_20180130_1623"), - ("machines", "0077_auto_20180409_2243"), - ("machines", "0078_auto_20180415_1252"), - ("machines", "0079_auto_20180416_0107"), - ("machines", "0080_auto_20180502_2334"), - ("machines", "0081_auto_20180521_1413"), - ("machines", "0082_auto_20180525_2209"), - ("machines", "0083_remove_duplicate_rights"), - ("machines", "0084_dname"), - ("machines", "0085_sshfingerprint"), - ("machines", "0086_role"), - ("machines", "0087_dnssec"), - ("machines", "0088_iptype_prefix_v6_length"), - ("machines", "0089_auto_20180805_1148"), - ("machines", "0090_auto_20180805_1459"), - ("machines", "0091_auto_20180806_2310"), - ("machines", "0092_auto_20180807_0926"), - ("machines", "0093_auto_20180807_1115"), - ("machines", "0094_auto_20180815_1918"), - ("machines", "0095_auto_20180919_2225"), - ("machines", "0096_auto_20181013_1417"), - ("machines", "0097_extension_dnssec"), - ("machines", "0098_auto_20190102_1745"), - ("machines", "0099_role_recursive_dns"), - ("machines", "0100_auto_20190102_1753"), - ("machines", "0101_auto_20190108_1623"), - ("machines", "0102_auto_20190303_1611"), - ("machines", "0103_auto_20191002_2222"), - ("machines", "0104_auto_20191002_2231"), - ("machines", "0105_dname_ttl"), - ("machines", "0106_auto_20191120_0159"), - ("machines", "0107_fix_lowercase_domain"), - ("machines", "0108_ipv6list_active"), - ("preferences", "0001_initial"), - ("preferences", "0002_auto_20170625_1923"), - ("preferences", "0003_optionaluser_solde_negatif"), - ("preferences", "0004_assooption_services"), - ("preferences", "0005_auto_20170824_0139"), - ("preferences", "0006_auto_20170824_0143"), - ("preferences", "0007_auto_20170824_2056"), - ("preferences", "0008_auto_20170824_2122"), - ("preferences", "0009_assooption_utilisateur_asso"), - ("preferences", "0010_auto_20170825_0459"), - ("preferences", "0011_auto_20170825_2307"), - ("preferences", "0012_generaloption_req_expire_hrs"), - ("preferences", "0013_generaloption_site_name"), - ("preferences", "0014_generaloption_email_from"), - ("preferences", "0015_optionaltopologie_radius_general_policy"), - ("preferences", "0016_auto_20170902_1520"), - ("preferences", "0017_mailmessageoption"), - ("preferences", "0018_optionaltopologie_mac_autocapture"), - ("preferences", "0019_remove_optionaltopologie_mac_autocapture"), - ("preferences", "0020_optionalmachine_ipv6"), - ("preferences", "0021_auto_20171015_1741"), - ("preferences", "0022_auto_20171015_1758"), - ("preferences", "0023_auto_20171015_2033"), - ("preferences", "0024_optionaluser_all_can_create"), - ("preferences", "0025_auto_20171231_2142"), - ("preferences", "0025_generaloption_general_message"), - ("preferences", "0026_auto_20171216_0401"), - ("preferences", "0027_merge_20180106_2019"), - ("preferences", "0028_assooption_description"), - ("preferences", "0028_auto_20180111_1129"), - ("preferences", "0028_auto_20180128_2203"), - ("preferences", "0029_auto_20180111_1134"), - ("preferences", "0029_auto_20180318_0213"), - ("preferences", "0029_auto_20180318_1005"), - ("preferences", "0030_auto_20180111_2346"), - ("preferences", "0030_merge_20180320_1419"), - ("preferences", "0031_auto_20180323_0218"), - ("preferences", "0031_optionaluser_self_adhesion"), - ("preferences", "0032_optionaluser_min_online_payment"), - ("preferences", "0032_optionaluser_shell_default"), - ("preferences", "0033_accueiloption"), - ("preferences", "0033_generaloption_gtu_sum_up"), - ("preferences", "0034_auto_20180114_2025"), - ("preferences", "0034_auto_20180416_1120"), - ("preferences", "0035_auto_20180114_2132"), - ("preferences", "0035_optionaluser_allow_self_subscription"), - ("preferences", "0036_auto_20180114_2141"), - ("preferences", "0037_auto_20180114_2156"), - ("preferences", "0038_auto_20180114_2209"), - ("preferences", "0039_auto_20180115_0003"), - ("preferences", "0040_auto_20180129_1745"), - ("preferences", "0041_merge_20180130_0052"), - ("preferences", "0042_auto_20180222_1743"), - ("preferences", "0043_optionalmachine_create_machine"), - ("preferences", "0044_remove_payment_pass"), - ("preferences", "0045_remove_unused_payment_fields"), - ("preferences", "0046_optionaluser_mail_extension"), - ("preferences", "0047_mailcontact"), - ("preferences", "0048_auto_20180811_1515"), - ("preferences", "0049_optionaluser_self_change_shell"), - ("preferences", "0050_auto_20180818_1329"), - ("preferences", "0051_auto_20180919_2225"), - ("preferences", "0052_optionaluser_delete_notyetactive"), - ("preferences", "0053_optionaluser_self_change_room"), - ("preferences", "0055_generaloption_main_site_url"), - ("preferences", "0056_1_radiusoption"), - ("preferences", "0056_2_radiusoption"), - ("preferences", "0056_3_radiusoption"), - ("preferences", "0056_4_radiusoption"), - ("preferences", "0057_optionaluser_all_users_active"), - ("preferences", "0058_auto_20190108_1650"), - ("preferences", "0059_auto_20190120_1739"), - ("preferences", "0060_auto_20190712_1821"), - ("preferences", "0061_optionaluser_allow_archived_connexion"), - ("preferences", "0062_auto_20190910_1909"), - ("preferences", "0063_mandate"), - ("preferences", "0064_auto_20191008_1335"), - ("preferences", "0065_auto_20191010_1227"), - ("preferences", "0066_optionalmachine_default_dns_ttl"), - ("preferences", "0067_auto_20191120_0159"), - ("preferences", "0068_optionaluser_allow_set_password_during_user_creation"), - ("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"), - ("preferences", "0070_auto_20200419_0225"), - ("preferences", "0071_optionaluser_self_change_pseudo"), - ("topologie", "0001_initial"), - ("topologie", "0002_auto_20160703_1118"), - ("topologie", "0003_room"), - ("topologie", "0004_auto_20160703_1122"), - ("topologie", "0005_auto_20160703_1123"), - ("topologie", "0006_auto_20160703_1129"), - ("topologie", "0007_auto_20160703_1148"), - ("topologie", "0008_port_room"), - ("topologie", "0009_auto_20160703_1200"), - ("topologie", "0010_auto_20160704_2148"), - ("topologie", "0011_auto_20160704_2153"), - ("topologie", "0012_port_machine_interface"), - ("topologie", "0013_port_related"), - ("topologie", "0014_auto_20160706_1238"), - ("topologie", "0015_auto_20160706_1452"), - ("topologie", "0016_auto_20160706_1531"), - ("topologie", "0017_auto_20160718_1141"), - ("topologie", "0018_room_details"), - ("topologie", "0019_auto_20161026_1348"), - ("topologie", "0020_auto_20161119_0033"), - ("topologie", "0021_port_radius"), - ("topologie", "0022_auto_20161211_1622"), - ("topologie", "0023_auto_20170817_1654"), - ("topologie", "0023_auto_20170826_1530"), - ("topologie", "0024_auto_20170818_1021"), - ("topologie", "0024_auto_20170826_1800"), - ("topologie", "0025_merge_20170902_1242"), - ("topologie", "0026_auto_20170902_1245"), - ("topologie", "0027_auto_20170905_1442"), - ("topologie", "0028_auto_20170913_1503"), - ("topologie", "0029_auto_20171002_0334"), - ("topologie", "0030_auto_20171004_0235"), - ("topologie", "0031_auto_20171015_2033"), - ("topologie", "0032_auto_20171026_0338"), - ("topologie", "0033_auto_20171231_1743"), - ("topologie", "0034_borne"), - ("topologie", "0035_auto_20180324_0023"), - ("topologie", "0036_transferborne"), - ("topologie", "0037_auto_20180325_0127"), - ("topologie", "0038_transfersw"), - ("topologie", "0039_port_new_switch"), - ("topologie", "0040_transferports"), - ("topologie", "0041_transferportsw"), - ("topologie", "0042_transferswitch"), - ("topologie", "0043_renamenewswitch"), - ("topologie", "0044_auto_20180326_0002"), - ("topologie", "0045_auto_20180326_0123"), - ("topologie", "0046_auto_20180326_0129"), - ("topologie", "0047_ap_machine"), - ("topologie", "0048_ap_machine"), - ("topologie", "0049_switchs_machine"), - ("topologie", "0050_port_new_switch"), - ("topologie", "0051_switchs_machine"), - ("topologie", "0052_transferports"), - ("topologie", "0053_finalsw"), - ("topologie", "0054_auto_20180326_1742"), - ("topologie", "0055_auto_20180329_0431"), - ("topologie", "0056_building_switchbay"), - ("topologie", "0057_auto_20180408_0316"), - ("topologie", "0058_remove_switch_location"), - ("topologie", "0059_auto_20180415_2249"), - ("topologie", "0060_server"), - ("topologie", "0061_portprofile"), - ("topologie", "0062_auto_20180815_1918"), - ("topologie", "0063_auto_20180919_2225"), - ("topologie", "0064_switch_automatic_provision"), - ("topologie", "0065_auto_20180927_1836"), - ("topologie", "0066_modelswitch_commercial_name"), - ("topologie", "0067_auto_20181230_1819"), - ("topologie", "0068_auto_20190102_1758"), - ("topologie", "0069_auto_20190108_1439"), - ("topologie", "0070_auto_20190218_1743"), - ("topologie", "0071_auto_20190218_1936"), - ("topologie", "0072_auto_20190720_2318"), - ("topologie", "0073_auto_20191120_0159"), - ("topologie", "0074_auto_20200419_1640"), - ] - - operations = [ - migrations.CreateModel( - name='LdapServiceUser', - fields=[ - ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), - ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='LdapServiceUserGroup', - fields=[ - ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), - ('members', ldapdb.models.fields.ListField(blank=True, db_column='member')), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='LdapUser', - fields=[ - ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), - ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), - ('uid', ldapdb.models.fields.CharField(db_column='uid', max_length=200)), - ('uidNumber', ldapdb.models.fields.IntegerField(db_column='uidNumber', unique=True)), - ('sn', ldapdb.models.fields.CharField(db_column='sn', max_length=200)), - ('login_shell', ldapdb.models.fields.CharField(blank=True, db_column='loginShell', max_length=200, null=True)), - ('mail', ldapdb.models.fields.CharField(db_column='mail', max_length=200)), - ('given_name', ldapdb.models.fields.CharField(db_column='givenName', max_length=200)), - ('home_directory', ldapdb.models.fields.CharField(db_column='homeDirectory', max_length=200)), - ('display_name', ldapdb.models.fields.CharField(blank=True, db_column='displayName', max_length=200, null=True)), - ('dialupAccess', ldapdb.models.fields.CharField(db_column='dialupAccess', max_length=200)), - ('sambaSID', ldapdb.models.fields.IntegerField(db_column='sambaSID', unique=True)), - ('user_password', ldapdb.models.fields.CharField(blank=True, db_column='userPassword', max_length=200, null=True)), - ('sambat_nt_password', ldapdb.models.fields.CharField(blank=True, db_column='sambaNTPassword', max_length=200, null=True)), - ('macs', ldapdb.models.fields.ListField(blank=True, db_column='radiusCallingStationId', max_length=200, null=True)), - ('shadowexpire', ldapdb.models.fields.CharField(blank=True, db_column='shadowExpire', max_length=200, null=True)), - ], - options={ - 'abstract': False, - }, - ), - migrations.CreateModel( - name='LdapUserGroup', - fields=[ - ('dn', ldapdb.models.fields.CharField(max_length=200, serialize=False)), - ('gid', ldapdb.models.fields.IntegerField(db_column='gidNumber')), - ('members', ldapdb.models.fields.ListField(blank=True, db_column='memberUid')), - ('name', ldapdb.models.fields.CharField(db_column='cn', max_length=200, primary_key=True, serialize=False)), - ], - options={ - 'abstract': False, - }, - ), - migrations.AlterField( - model_name='ldapserviceuser', - name='dn', - field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name='ldapserviceusergroup', - name='dn', - field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name='ldapuser', - name='dn', - field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name='ldapusergroup', - name='dn', - field=ldapdb.models.fields.CharField(max_length=200, primary_key=True, serialize=False), - ), - ] diff --git a/users/migrations/0004_auto_20210110_1811.py b/users/migrations/0096_auto_20210110_1811.py similarity index 86% rename from users/migrations/0004_auto_20210110_1811.py rename to users/migrations/0096_auto_20210110_1811.py index f6179f01..6a6fd720 100644 --- a/users/migrations/0004_auto_20210110_1811.py +++ b/users/migrations/0096_auto_20210110_1811.py @@ -8,7 +8,7 @@ from django.db import migrations class Migration(migrations.Migration): dependencies = [ - ('users', '0003_ldapserviceuser_ldapserviceusergroup_ldapuser_ldapusergroup'), + ('users', '0095_user_theme'), ] operations = [ From cbfe9d8cfe8f99f2e31986f8eaa52556f7231b9c Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 11 Jan 2021 22:02:35 +0100 Subject: [PATCH 471/490] BuG fixes :) --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3bf2a23..024c96ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,7 +60,7 @@ Here is a list of noteworthy features brought by this update: * [!578](https://gitlab.federez.net/re2o/re2o/-/merge_requests/578) : Migrations squashed to ease the installation process. * [!582](https://gitlab.federez.net/re2o/re2o/-/merge_requests/582): Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account). * [!589](https://gitlab.federez.net/re2o/re2o/-/merge_requests/589): Move LDAP to a separate optional app. -* Plenty of bux fixes. +* Plenty of bug fixes. You can view the full list of closed issues [here](https://gitlab.federez.net/re2o/re2o/-/issues?scope=all&state=all&milestone_title=Re2o 2.9). From de0d48a4f39b59cc10087dc94f4ff761101647fb Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Mon, 11 Jan 2021 22:14:08 +0100 Subject: [PATCH 472/490] Add missing update step details in CHANGELOG.md --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 024c96ce..88a30368 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,15 +30,16 @@ sudo pip3 install -r pip_requirements.txt python3 manage.py collectstatic ``` -### MR 589: Move ldap to optional app +### MR 589: Move LDAP to optional app -Add `ldap_sync` to your optional apps in your local settings if you want to keep using the LDAP synchronisation. +Add `ldap_sync` to your optional apps in your local settings (`re2o/settings_local.py`) if you want to keep using the LDAP synchronisation. ### Final steps As usual, run the following commands after updating: ```bash python3 manage.py migrate +python3 manage.py compilemessages sudo service apache2 reload ``` From 5a8f5b73c41e07cefa191a5445ef2358af4af4da Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 11 Jan 2021 23:17:48 +0100 Subject: [PATCH 473/490] Thanks to our contributors --- .mailmap | 84 ++++++++++++++++++++++++++++++++++++++++++++ re2o/contributors.py | 79 ++++++++++++++++++++++------------------- 2 files changed, 126 insertions(+), 37 deletions(-) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000..e42dc8b5 --- /dev/null +++ b/.mailmap @@ -0,0 +1,84 @@ +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Gabriel Detraz +Maël Kervella +Maël Kervella +Maël Kervella +Jean-Romain Garnier +Jean-Romain Garnier +Hugo Levy-Falk +Hugo Levy-Falk +Hugo Levy-Falk +Hugo Levy-Falk +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Arthur Grisel-Davy +Lara Kermarec +Lara Kermarec +Lara Kermarec +Lara Kermarec +Lara Kermarec +Lara Kermarec +Augustin Lemesle +Augustin Lemesle +Corentin Canebier +Corentin Canebier +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Root `root` Root +Yoann Piétri +Yoann Piétri +Guillaume Goessel +Guillaume Goessel +Guillaume Goessel +Guillaume Goessel +David Sinquin +David Sinquin +David Sinquin +Matthieu Michelet +Matthieu Michelet +Jean-Marie Mineau +~anonymised~ +~anonymised~ +Thibault de Boutray +Thibault de Boutray +Fardale +Fardale +Laouen Fernet +Laouen Fernet FERNET Laouen +Bombar Maxime +Leïla Bekaddour +Leïla Bekaddour +edpibu +edpibu +Éloi Alain +Éloi Alain +Gabriel Le Bouder +Gabriel Le Bouder +Charlie Jacomme +Charlie Jacomme diff --git a/re2o/contributors.py b/re2o/contributors.py index bdcf5f9a..b8b04eb2 100644 --- a/re2o/contributors.py +++ b/re2o/contributors.py @@ -4,40 +4,45 @@ A list of the proud contributors to Re2o """ CONTRIBUTORS = [ - "Gabriel 'chibrac' Detraz", - "Hugo 'klafyvel' Levy-Falk", - "Maël 'MoaMoaK' Kervella", - "Arthur 'Grizzly' Grisel-Davy", - "Augustin 'Dalahro' Lemesle", - "Laouen 'volgarr' Fernet", - "Lara 'lhark' Kermarec", - "Alexandre 'erdnaxe' Ioss", - "Charlie Jacomme", - "Root 'root' Root", - "Yoann 'nanoy' Pietri", - "Maxime Bombar", - "Matthieu 'lebanni' Michelet", - "Guillaume 'guimoz' Goessel", - "edpibu", - "Jean-Romain 'lerennais' Garnier", - "David '5-1' Sinquin", - "Gabriel Le Bouder", - "Fardale", - "Éloi Alain", - "Simon Brelivet", - "Antoine Vintache", - "Benjamin Graillot", - "Thibault 'tipunchetrhum' Deboutray", - "Joanne Steiner", - "Delphine Salvy", - "Pierre Cadart", - "'Krokmou'", - "Vincent Le Gallic", - "Hugo 'shaka' Hervieux", - "Pierre-Antoine Comby", - "Nymous", - "Mikachu", - "Baptiste 'B' Fournier", - "Gwenael Le Hir", - "Daniel Stan", -] + 'Gabriel Detraz', + 'Hugo Levy-falk', + 'Maël Kervella', + 'Jean-romain Garnier', + 'Arthur Grisel-davy', + 'Laouen Fernet', + 'Augustin Lemesle', + 'Lara Kermarec', + 'Root `root` Root', + 'Alexandre Iooss', + 'Yoann Piétri', + 'Charlie Jacomme', + 'Corentin Canebier', + 'Bombar Maxime', + 'Guillaume Goessel', + 'Matthieu Michelet', + 'Edpibu', + 'Fardale', + 'Jean-marie Mineau', + 'David Sinquin', + 'Gabriel Le Bouder', + 'Simon Brélivet', + '~anonymised~', + 'Leïla Bekaddour', + 'Éloi Alain', + 'Benjamin Graillot', + 'Pierre Cadart', + 'Antoine Vintache', + 'Thibault De Boutray', + 'Delphine Salvy', + 'Joanne Steiner', + 'Krokmou', + 'B', + 'Daniel Stan', + 'Gwenael Le Hir', + 'Hugo Hervieux', + 'Mikachu', + 'Nymous', + 'Pierre-antoine Comby', + 'Vincent Le Gallic', + 'Esum', +] \ No newline at end of file From 724e291bd6cba39307a4990c58e885f8ba4620e9 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Mon, 11 Jan 2021 23:30:53 +0100 Subject: [PATCH 474/490] I forgot Esum. --- .mailmap | 2 ++ re2o/contributors.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index e42dc8b5..fc7dde8f 100644 --- a/.mailmap +++ b/.mailmap @@ -82,3 +82,5 @@ Gabriel Le Bouder Gabriel Le Bouder Charlie Jacomme Charlie Jacomme +Benjamin Graillot +Benjamin Graillot diff --git a/re2o/contributors.py b/re2o/contributors.py index b8b04eb2..cbca94f0 100644 --- a/re2o/contributors.py +++ b/re2o/contributors.py @@ -27,9 +27,9 @@ CONTRIBUTORS = [ 'Gabriel Le Bouder', 'Simon Brélivet', '~anonymised~', + 'Benjamin Graillot', 'Leïla Bekaddour', 'Éloi Alain', - 'Benjamin Graillot', 'Pierre Cadart', 'Antoine Vintache', 'Thibault De Boutray', @@ -44,5 +44,4 @@ CONTRIBUTORS = [ 'Nymous', 'Pierre-antoine Comby', 'Vincent Le Gallic', - 'Esum', ] \ No newline at end of file From 7b941eee124bdcdd86b63d609e9ade1dc04a153f Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Tue, 12 Jan 2021 19:08:17 +0100 Subject: [PATCH 475/490] Fix: Forgotten legacy `ldap_sync` calls in `machines` and `cotisations`. --- cotisations/models.py | 10 ++++++---- machines/models.py | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cotisations/models.py b/cotisations/models.py index 5dc18e4f..74be4285 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -50,6 +50,8 @@ from preferences.models import CotisationsOption from machines.models import regen from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin +import users.signals +import users.models from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher from cotisations.validators import check_no_balance @@ -356,7 +358,7 @@ def facture_post_save(**kwargs): if facture.valid: user = facture.user user.set_active() - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False) @receiver(post_delete, sender=Facture) @@ -365,7 +367,7 @@ def facture_post_delete(**kwargs): Synchronise the LDAP user after an invoice has been deleted. """ user = kwargs["instance"].user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False) class CustomInvoice(BaseInvoice): @@ -661,7 +663,7 @@ def vente_post_save(**kwargs): purchase.cotisation.save() user = purchase.facture.facture.user user.set_active() - user.ldap_sync(base=True, access_refresh=True, mac_refresh=False) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False) # TODO : change vente to purchase @@ -677,7 +679,7 @@ def vente_post_delete(**kwargs): return if purchase.type_cotisation: user = invoice.user - user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False) class Article(RevMixin, AclMixin, models.Model): diff --git a/machines/models.py b/machines/models.py index f4991522..cc1e64d3 100644 --- a/machines/models.py +++ b/machines/models.py @@ -58,6 +58,7 @@ from netaddr import ( import preferences.models import users.models +import users.signals from re2o.field_permissions import FieldPermissionModelMixin from re2o.mixins import AclMixin, RevMixin @@ -2541,7 +2542,7 @@ class OuverturePort(RevMixin, AclMixin, models.Model): def machine_post_save(**kwargs): """Synchronise LDAP and regen firewall/DHCP after a machine is edited.""" user = kwargs["instance"].user - user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) regen("dhcp") regen("mac_ip_list") @@ -2551,7 +2552,7 @@ def machine_post_delete(**kwargs): """Synchronise LDAP and regen firewall/DHCP after a machine is deleted.""" machine = kwargs["instance"] user = machine.user - user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) regen("dhcp") regen("mac_ip_list") @@ -2564,7 +2565,7 @@ def interface_post_save(**kwargs): interface = kwargs["instance"] interface.sync_ipv6() user = interface.machine.user - user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) # Regen services regen("dhcp") regen("mac_ip_list") @@ -2580,7 +2581,7 @@ def interface_post_delete(**kwargs): """ interface = kwargs["instance"] user = interface.machine.user - user.ldap_sync(base=False, access_refresh=False, mac_refresh=True) + users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=False, mac_refresh=True) @receiver(post_save, sender=IpType) From 17aa0afd09eefb42712ea120d37a5a0529525177 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 15 Jan 2021 19:39:05 +0100 Subject: [PATCH 476/490] fix: Subtable alternating color in subscription list --- cotisations/templates/cotisations/aff_cotisations.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cotisations/templates/cotisations/aff_cotisations.html b/cotisations/templates/cotisations/aff_cotisations.html index e48a6e2b..05be4861 100644 --- a/cotisations/templates/cotisations/aff_cotisations.html +++ b/cotisations/templates/cotisations/aff_cotisations.html @@ -60,7 +60,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {{ facture.user }} - +
    {% for article in facture.name_detailed %} - - - - - - {% endfor %} -
    From a2c0ab66f0368539c0ad257df01739f0973b790b Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 15 Jan 2021 19:40:04 +0100 Subject: [PATCH 477/490] fix: Whitelist and ticket list responsiveness --- tickets/templates/tickets/aff_tickets.html | 2 +- users/templates/users/aff_whitelists.html | 79 +++++++++++----------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/tickets/templates/tickets/aff_tickets.html b/tickets/templates/tickets/aff_tickets.html index beaadc4f..caf82cf3 100644 --- a/tickets/templates/tickets/aff_tickets.html +++ b/tickets/templates/tickets/aff_tickets.html @@ -46,7 +46,7 @@ with this program; if not, write to the Free Software Foundation, Inc., -
    +
    diff --git a/users/templates/users/aff_whitelists.html b/users/templates/users/aff_whitelists.html index fdfd0b76..40d55d28 100644 --- a/users/templates/users/aff_whitelists.html +++ b/users/templates/users/aff_whitelists.html @@ -30,43 +30,44 @@ with this program; if not, write to the Free Software Foundation, Inc., {% load acl %} {% load logs_extra %} -
    - - - {% trans "User" as tr_user %} - - - {% trans "Start date" as tr_start %} - - {% trans "End date" as tr_end %} - - - - - {% for whitelist in white_list %} - {% if whitelist.is_active %} - - {% else %} - +
    +
    {% include 'buttons/sort.html' with prefix='white' col="user" text=tr_user %}{% trans "Reason" %}{% include 'buttons/sort.html' with prefix='white' col="start" text=tr_start %}{% include 'buttons/sort.html' with prefix='white' col="end" text=tr_end %}
    + + + {% trans "User" as tr_user %} + + + {% trans "Start date" as tr_start %} + + {% trans "End date" as tr_end %} + + + + + {% for whitelist in white_list %} + {% if whitelist.is_active %} + + {% else %} + + {% endif %} + + + + + + + {% endfor %} +
    {% include 'buttons/sort.html' with prefix='white' col="user" text=tr_user %}{% trans "Reason" %}{% include 'buttons/sort.html' with prefix='white' col="start" text=tr_start %}{% include 'buttons/sort.html' with prefix='white' col="end" text=tr_end %}
    {{ whitelist.user }}{{ whitelist.raison }}{{ whitelist.date_start }}{{ whitelist.date_end }} + {% can_delete whitelist %} + {% include 'buttons/suppr.html' with href='users:del-whitelist' id=whitelist.id %} + {% acl_end %} + {% history_button whitelist %} + {% can_edit whitelist %} + {% include 'buttons/edit.html' with href='users:edit-whitelist' id=whitelist.id %} + {% acl_end %} +
    + + {% if white_list.paginator %} + {% include 'pagination.html' with list=white_list %} {% endif %} -
    {{ whitelist.user }}{{ whitelist.raison }}{{ whitelist.date_start }}{{ whitelist.date_end }} - {% can_delete whitelist %} - {% include 'buttons/suppr.html' with href='users:del-whitelist' id=whitelist.id %} - {% acl_end %} - {% history_button whitelist %} - {% can_edit whitelist %} - {% include 'buttons/edit.html' with href='users:edit-whitelist' id=whitelist.id %} - {% acl_end %} -
    - -{% if white_list.paginator %} - {% include 'pagination.html' with list=white_list %} -{% endif %} - + From fdb0b0b10f596561480f9349217633c62252d361 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 15 Jan 2021 19:51:11 +0100 Subject: [PATCH 478/490] fix: Use bootstrap class for fa-check and fa-times color instead of hardcoded value --- cotisations/templates/cotisations/aff_cost_estimate.html | 4 ++-- re2o/templatetags/design.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cotisations/templates/cotisations/aff_cost_estimate.html b/cotisations/templates/cotisations/aff_cost_estimate.html index fe654744..269dc6af 100644 --- a/cotisations/templates/cotisations/aff_cost_estimate.html +++ b/cotisations/templates/cotisations/aff_cost_estimate.html @@ -73,9 +73,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
    {{ estimate.id }} {% if estimate.final_invoice %} - + {% else %} - + {% endif %} diff --git a/re2o/templatetags/design.py b/re2o/templatetags/design.py index 488b1489..45739133 100644 --- a/re2o/templatetags/design.py +++ b/re2o/templatetags/design.py @@ -31,9 +31,9 @@ def tick(valeur, autoescape=False): if isinstance(valeur, bool): if valeur == True: - result = '' + result = '' else: - result = '' + result = '' return mark_safe(result) else: # if the value is not a boolean, display it as if tick was not called From 1a9d3d0824ecbbeb7807e233067c537fb4b77e81 Mon Sep 17 00:00:00 2001 From: Hugo Levy-Falk Date: Sun, 24 Jan 2021 11:07:19 +0100 Subject: [PATCH 479/490] fix: Permission DoesNotExist when accessing BaseInvoice history See the issue for details. Closes #317 . --- .../migrations/0003_auto_20210124_1105.py | 19 +++++++++++++++++++ cotisations/models.py | 6 ++++++ 2 files changed, 25 insertions(+) create mode 100644 cotisations/migrations/0003_auto_20210124_1105.py diff --git a/cotisations/migrations/0003_auto_20210124_1105.py b/cotisations/migrations/0003_auto_20210124_1105.py new file mode 100644 index 00000000..63e7dec5 --- /dev/null +++ b/cotisations/migrations/0003_auto_20210124_1105.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2021-01-24 10:05 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('cotisations', '0002_foreign_keys'), + ] + + operations = [ + migrations.AlterModelOptions( + name='baseinvoice', + options={'permissions': (('view_baseinvoice', 'Can view an base invoice object'),)}, + ), + ] diff --git a/cotisations/models.py b/cotisations/models.py index 74be4285..759f5320 100644 --- a/cotisations/models.py +++ b/cotisations/models.py @@ -60,6 +60,12 @@ from cotisations.validators import check_no_balance class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): date = models.DateTimeField(auto_now_add=True, verbose_name=_("date")) + class Meta: + abstract = False + permissions = ( + ("view_baseinvoice", _("Can view an base invoice object")), + ) + # TODO : change prix to price def prix(self): """ From 69228f0ae42d4ad4182d4eb42990cd3d345d5fc0 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 24 Jan 2021 11:54:44 +0100 Subject: [PATCH 480/490] chore(trans): Regenerate all django.po file --- api/locale/fr/LC_MESSAGES/django.po | 4 +- cotisations/locale/fr/LC_MESSAGES/django.po | 222 ++++++------- logs/locale/fr/LC_MESSAGES/django.po | 36 +-- machines/locale/fr/LC_MESSAGES/django.po | 340 +++++++++---------- multi_op/locale/fr/LC_MESSAGES/django.po | 2 +- preferences/locale/fr/LC_MESSAGES/django.po | 208 ++++++------ re2o/locale/fr/LC_MESSAGES/django.po | 53 +-- search/locale/fr/LC_MESSAGES/django.po | 2 +- templates/locale/fr/LC_MESSAGES/django.po | 20 +- tickets/locale/fr/LC_MESSAGES/django.po | 8 +- topologie/locale/fr/LC_MESSAGES/django.po | 264 +++++++-------- users/locale/fr/LC_MESSAGES/django.po | 341 ++++++++++---------- 12 files changed, 765 insertions(+), 735 deletions(-) diff --git a/api/locale/fr/LC_MESSAGES/django.po b/api/locale/fr/LC_MESSAGES/django.po index 7e0244f5..17d3a1d9 100644 --- a/api/locale/fr/LC_MESSAGES/django.po +++ b/api/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -31,7 +31,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: api/acl.py:77 +#: api/acl.py:52 msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 52b0d104..004cd5f4 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Yoann Piétri \n" "Language: fr_FR\n" @@ -33,83 +33,83 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: cotisations/forms.py:70 cotisations/forms.py:296 +#: cotisations/forms.py:71 cotisations/forms.py:301 msgid "Select a payment method" msgstr "Sélectionnez un moyen de paiement" -#: cotisations/forms.py:73 cotisations/models.py:695 +#: cotisations/forms.py:74 cotisations/models.py:706 msgid "Member" msgstr "Adhérent" -#: cotisations/forms.py:74 +#: cotisations/forms.py:75 msgid "Select the proprietary member" msgstr "Sélectionnez l'adhérent propriétaire" -#: cotisations/forms.py:75 +#: cotisations/forms.py:76 msgid "Validated invoice" msgstr "Facture validée" -#: cotisations/forms.py:87 +#: cotisations/forms.py:92 msgid "A payment method must be specified." msgstr "Un moyen de paiement doit être renseigné." -#: cotisations/forms.py:98 +#: cotisations/forms.py:103 #: cotisations/templates/cotisations/aff_article.html:33 #: cotisations/templates/cotisations/facture.html:64 msgid "Article" msgstr "Article" -#: cotisations/forms.py:101 +#: cotisations/forms.py:106 #: cotisations/templates/cotisations/edit_facture.html:50 msgid "Quantity" msgstr "Quantité" -#: cotisations/forms.py:119 +#: cotisations/forms.py:124 msgid "Discount is in percentage." msgstr "La réduction est en pourcentage." -#: cotisations/forms.py:122 cotisations/templates/cotisations/facture.html:75 +#: cotisations/forms.py:127 cotisations/templates/cotisations/facture.html:75 msgid "Discount" msgstr "Réduction" -#: cotisations/forms.py:139 +#: cotisations/forms.py:144 #, python-format msgid "{}% discount" msgstr "{}% de réduction" -#: cotisations/forms.py:139 +#: cotisations/forms.py:144 msgid "{} € discount" msgstr "{} € de réduction" -#: cotisations/forms.py:176 +#: cotisations/forms.py:181 msgid "Article name" msgstr "Nom de l'article" -#: cotisations/forms.py:187 +#: cotisations/forms.py:192 msgid "Current articles" msgstr "Articles actuels" -#: cotisations/forms.py:216 +#: cotisations/forms.py:221 msgid "Payment method name" msgstr "Nom du moyen de paiement" -#: cotisations/forms.py:229 +#: cotisations/forms.py:234 msgid "Current payment methods" msgstr "Moyens de paiement actuels" -#: cotisations/forms.py:256 +#: cotisations/forms.py:261 msgid "Bank name" msgstr "Nom de la banque" -#: cotisations/forms.py:269 +#: cotisations/forms.py:274 msgid "Current banks" msgstr "Banques actuelles" -#: cotisations/forms.py:288 +#: cotisations/forms.py:293 msgid "Amount" msgstr "Montant" -#: cotisations/forms.py:290 +#: cotisations/forms.py:295 #: cotisations/templates/cotisations/aff_cost_estimate.html:42 #: cotisations/templates/cotisations/aff_cotisations.html:44 #: cotisations/templates/cotisations/aff_custom_invoice.html:42 @@ -117,7 +117,7 @@ msgstr "Montant" msgid "Payment method" msgstr "Moyen de paiement" -#: cotisations/forms.py:314 +#: cotisations/forms.py:319 #, python-format msgid "" "Requested amount is too high. Your balance can't exceed " @@ -126,52 +126,58 @@ msgstr "" "Le montant demandé est trop grand. Votre solde ne peut excéder " "%(max_online_balance)s €." -#: cotisations/models.py:59 +#: cotisations/models.py:61 msgid "date" msgstr "date" -#: cotisations/models.py:140 +#: cotisations/models.py:66 +#, fuzzy +#| msgid "Can view an invoice object" +msgid "Can view an base invoice object" +msgstr "Peut voir un objet facture" + +#: cotisations/models.py:148 msgid "cheque number" msgstr "numéro de chèque" -#: cotisations/models.py:143 +#: cotisations/models.py:151 msgid "validated" msgstr "validé" -#: cotisations/models.py:145 +#: cotisations/models.py:153 msgid "controlled" msgstr "contrôlé" -#: cotisations/models.py:151 +#: cotisations/models.py:159 msgid "Can edit the \"controlled\" state" msgstr "Peut modifier l'état \"contrôlé\"" -#: cotisations/models.py:152 +#: cotisations/models.py:160 msgid "Can view an invoice object" msgstr "Peut voir un objet facture" -#: cotisations/models.py:153 +#: cotisations/models.py:161 msgid "Can edit all the previous invoices" msgstr "Peut modifier toutes les factures précédentes" -#: cotisations/models.py:155 cotisations/models.py:451 cotisations/views.py:380 +#: cotisations/models.py:163 cotisations/models.py:458 cotisations/views.py:380 #: cotisations/views.py:575 msgid "invoice" msgstr "facture" -#: cotisations/models.py:156 +#: cotisations/models.py:164 msgid "invoices" msgstr "factures" -#: cotisations/models.py:170 +#: cotisations/models.py:178 msgid "You don't have the right to edit an invoice." msgstr "Vous n'avez pas le droit de modifier une facture." -#: cotisations/models.py:178 +#: cotisations/models.py:186 msgid "You don't have the right to edit this user's invoices." msgstr "Vous n'avez pas le droit de modifier les factures de cet utilisateur." -#: cotisations/models.py:187 +#: cotisations/models.py:195 msgid "" "You don't have the right to edit an invoice already controlled or " "invalidated." @@ -179,15 +185,15 @@ msgstr "" "Vous n'avez pas le droit de modifier une facture précédemment contrôlée ou " "invalidée." -#: cotisations/models.py:202 +#: cotisations/models.py:210 msgid "You don't have the right to delete an invoice." msgstr "Vous n'avez pas le droit de supprimer une facture." -#: cotisations/models.py:210 +#: cotisations/models.py:218 msgid "You don't have the right to delete this user's invoices." msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur." -#: cotisations/models.py:219 +#: cotisations/models.py:227 msgid "" "You don't have the right to delete an invoice already controlled or " "invalidated." @@ -195,123 +201,123 @@ msgstr "" "Vous n'avez pas le droit de supprimer une facture précédemment contrôlée ou " "invalidée." -#: cotisations/models.py:233 +#: cotisations/models.py:241 msgid "You don't have the right to view someone else's invoices history." msgstr "" "Vous n'avez pas le droit de voir l'historique des factures d'un autre " "utilisateur." -#: cotisations/models.py:241 +#: cotisations/models.py:249 msgid "The invoice has been invalidated." msgstr "La facture a été invalidée." -#: cotisations/models.py:256 +#: cotisations/models.py:264 msgid "You don't have the right to edit the \"controlled\" state." msgstr "Vous n'avez pas le droit de modifier le statut \"contrôlé\"." -#: cotisations/models.py:275 +#: cotisations/models.py:283 msgid "There are no payment methods that you can use." msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." -#: cotisations/models.py:281 +#: cotisations/models.py:289 msgid "There are no articles that you can buy." msgstr "Il n'y a pas d'articles que vous puissiez acheter." -#: cotisations/models.py:374 +#: cotisations/models.py:381 msgid "Can view a custom invoice object" msgstr "Peut voir un objet facture personnalisée" -#: cotisations/models.py:376 +#: cotisations/models.py:383 msgid "recipient" msgstr "destinataire" -#: cotisations/models.py:377 +#: cotisations/models.py:384 msgid "payment type" msgstr "type de paiement" -#: cotisations/models.py:378 +#: cotisations/models.py:385 msgid "address" msgstr "adresse" -#: cotisations/models.py:379 +#: cotisations/models.py:386 msgid "paid" msgstr "payé" -#: cotisations/models.py:380 +#: cotisations/models.py:387 msgid "remark" msgstr "remarque" -#: cotisations/models.py:385 +#: cotisations/models.py:392 msgid "Can view a cost estimate object" msgstr "Peut voir un objet devis" -#: cotisations/models.py:388 +#: cotisations/models.py:395 msgid "period of validity" msgstr "période de validité" -#: cotisations/models.py:423 +#: cotisations/models.py:430 msgid "You don't have the right to delete a cost estimate." msgstr "Vous n'avez pas le droit de supprimer un devis." -#: cotisations/models.py:429 +#: cotisations/models.py:436 msgid "The cost estimate has an invoice and can't be deleted." msgstr "Le devis a une facture et ne peut pas être supprimé." -#: cotisations/models.py:455 +#: cotisations/models.py:462 msgid "amount" msgstr "montant" -#: cotisations/models.py:460 +#: cotisations/models.py:467 msgid "article" msgstr "article" -#: cotisations/models.py:463 +#: cotisations/models.py:470 msgid "price" msgstr "prix" -#: cotisations/models.py:466 cotisations/models.py:716 +#: cotisations/models.py:473 cotisations/models.py:726 msgid "duration of the connection (in months)" msgstr "durée de la connexion (en mois)" -#: cotisations/models.py:471 cotisations/models.py:720 +#: cotisations/models.py:478 cotisations/models.py:730 msgid "" "duration of the connection (in days, will be added to duration in months)" msgstr "durée de la connexion (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:474 cotisations/models.py:708 +#: cotisations/models.py:482 cotisations/models.py:718 msgid "duration of the membership (in months)" msgstr "durée de l'adhésion (en mois)" -#: cotisations/models.py:479 cotisations/models.py:712 +#: cotisations/models.py:487 cotisations/models.py:722 msgid "" "duration of the membership (in days, will be added to duration in months)" msgstr "durée de l'adhésion (en jours, sera ajoutée à la durée en mois)" -#: cotisations/models.py:484 +#: cotisations/models.py:493 msgid "Can view a purchase object" msgstr "Peut voir un objet achat" -#: cotisations/models.py:485 +#: cotisations/models.py:494 msgid "Can edit all the previous purchases" msgstr "Peut modifier tous les achats précédents" -#: cotisations/models.py:487 cotisations/models.py:957 +#: cotisations/models.py:496 cotisations/models.py:970 msgid "purchase" msgstr "achat" -#: cotisations/models.py:488 +#: cotisations/models.py:497 msgid "purchases" msgstr "achats" -#: cotisations/models.py:563 +#: cotisations/models.py:572 msgid "You don't have the right to edit a purchase." msgstr "Vous n'avez pas le droit de modifier un achat." -#: cotisations/models.py:569 +#: cotisations/models.py:578 msgid "You don't have the right to edit this user's purchases." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." -#: cotisations/models.py:578 +#: cotisations/models.py:587 msgid "" "You don't have the right to edit a purchase already controlled or " "invalidated." @@ -319,15 +325,15 @@ msgstr "" "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:593 +#: cotisations/models.py:602 msgid "You don't have the right to delete a purchase." msgstr "Vous n'avez pas le droit de supprimer un achat." -#: cotisations/models.py:599 +#: cotisations/models.py:608 msgid "You don't have the right to delete this user's purchases." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." -#: cotisations/models.py:606 +#: cotisations/models.py:615 msgid "" "You don't have the right to delete a purchase already controlled or " "invalidated." @@ -335,150 +341,150 @@ msgstr "" "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "invalidé." -#: cotisations/models.py:622 +#: cotisations/models.py:631 msgid "You don't have the right to view someone else's purchase history." msgstr "" "Vous n'avez pas le droit de voir l'historique des achats d'un autre " "utilisateur." -#: cotisations/models.py:696 +#: cotisations/models.py:707 msgid "Club" msgstr "Club" -#: cotisations/models.py:697 +#: cotisations/models.py:708 msgid "Both of them" msgstr "Les deux" -#: cotisations/models.py:700 +#: cotisations/models.py:711 msgid "designation" msgstr "désignation" -#: cotisations/models.py:703 +#: cotisations/models.py:714 msgid "unit price" msgstr "prix unitaire" -#: cotisations/models.py:725 +#: cotisations/models.py:736 msgid "need membership to be purchased" msgstr "nécessite une adhésion pour être acheté" -#: cotisations/models.py:732 +#: cotisations/models.py:743 msgid "type of users concerned" msgstr "type d'utilisateurs concernés" -#: cotisations/models.py:735 cotisations/models.py:836 +#: cotisations/models.py:746 cotisations/models.py:847 msgid "is available for every user" msgstr "est disponible pour chaque utilisateur" -#: cotisations/models.py:742 +#: cotisations/models.py:753 msgid "Can view an article object" msgstr "Peut voir un objet article" -#: cotisations/models.py:743 +#: cotisations/models.py:754 msgid "Can buy every article" msgstr "Peut acheter chaque article" -#: cotisations/models.py:750 +#: cotisations/models.py:761 msgid "Solde is a reserved article name." msgstr "Solde est un nom d'article réservé." -#: cotisations/models.py:773 +#: cotisations/models.py:784 msgid "You can't buy this article." msgstr "Vous ne pouvez pas acheter cet article." -#: cotisations/models.py:816 +#: cotisations/models.py:827 msgid "Can view a bank object" msgstr "Peut voir un objet banque" -#: cotisations/models.py:817 +#: cotisations/models.py:828 msgid "bank" msgstr "banque" -#: cotisations/models.py:818 +#: cotisations/models.py:829 msgid "banks" msgstr "banques" -#: cotisations/models.py:834 +#: cotisations/models.py:845 msgid "method" msgstr "moyen" -#: cotisations/models.py:841 +#: cotisations/models.py:852 msgid "is user balance" msgstr "est solde utilisateur" -#: cotisations/models.py:842 +#: cotisations/models.py:853 msgid "There should be only one balance payment method." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." -#: cotisations/models.py:848 +#: cotisations/models.py:859 msgid "Can view a payment method object" msgstr "Peut voir un objet moyen de paiement" -#: cotisations/models.py:849 +#: cotisations/models.py:860 msgid "Can use every payment method" msgstr "Peut utiliser chaque moyen de paiement" -#: cotisations/models.py:851 +#: cotisations/models.py:862 msgid "payment method" msgstr "moyen de paiement" -#: cotisations/models.py:852 +#: cotisations/models.py:863 msgid "payment methods" msgstr "moyens de paiement" -#: cotisations/models.py:891 cotisations/payment_methods/comnpay/views.py:62 +#: cotisations/models.py:904 cotisations/payment_methods/comnpay/views.py:62 #, python-format msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." -#: cotisations/models.py:901 +#: cotisations/models.py:914 msgid "The invoice was created." msgstr "La facture a été créée." -#: cotisations/models.py:921 +#: cotisations/models.py:934 msgid "You can't use this payment method." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." -#: cotisations/models.py:940 +#: cotisations/models.py:953 msgid "No custom payment methods." msgstr "Pas de moyens de paiement personnalisés." -#: cotisations/models.py:959 +#: cotisations/models.py:973 msgid "start date for the connection" msgstr "date de début pour la connexion" -#: cotisations/models.py:960 +#: cotisations/models.py:975 msgid "end date for the connection" msgstr "date de fin pour la connexion" -#: cotisations/models.py:961 +#: cotisations/models.py:977 msgid "start date for the membership" msgstr "date de début pour l'adhésion" -#: cotisations/models.py:962 +#: cotisations/models.py:979 msgid "end date for the membership" msgstr "date de fin pour l'adhésion" -#: cotisations/models.py:966 +#: cotisations/models.py:983 msgid "Can view a subscription object" msgstr "Peut voir un objet cotisation" -#: cotisations/models.py:967 +#: cotisations/models.py:984 msgid "Can edit the previous subscriptions" msgstr "Peut modifier les cotisations précédentes" -#: cotisations/models.py:969 +#: cotisations/models.py:986 msgid "subscription" msgstr "cotisation" -#: cotisations/models.py:970 +#: cotisations/models.py:987 msgid "subscriptions" msgstr "cotisations" -#: cotisations/models.py:976 +#: cotisations/models.py:993 msgid "You don't have the right to edit a subscription." msgstr "Vous n'avez pas le droit de modifier une cotisation." -#: cotisations/models.py:985 +#: cotisations/models.py:1002 msgid "" "You don't have the right to edit a subscription already controlled or " "invalidated." @@ -486,11 +492,11 @@ msgstr "" "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:997 +#: cotisations/models.py:1014 msgid "You don't have the right to delete a subscription." msgstr "Vous n'avez pas le droit de supprimer une cotisation." -#: cotisations/models.py:1004 +#: cotisations/models.py:1021 msgid "" "You don't have the right to delete a subscription already controlled or " "invalidated." @@ -498,7 +504,7 @@ msgstr "" "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "ou invalidée." -#: cotisations/models.py:1020 +#: cotisations/models.py:1037 msgid "You don't have the right to view someone else's subscription history." msgstr "" "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " @@ -824,7 +830,7 @@ msgstr "" "Attention: voulez-vous vraiment supprimer cet objet %(objet_name)s " "( %(objet)s ) ?" -#: cotisations/templates/cotisations/edit_facture.html:31 +#: cotisations/templates/cotisations/edit_facture.html:30 #: cotisations/templates/cotisations/facture.html:30 msgid "Creation and editing of invoices" msgstr "Création et modification de factures" diff --git a/logs/locale/fr/LC_MESSAGES/django.po b/logs/locale/fr/LC_MESSAGES/django.po index 2bf4b6e7..5478e9a7 100644 --- a/logs/locale/fr/LC_MESSAGES/django.po +++ b/logs/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-23 16:01+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,31 +34,31 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: logs/forms.py:40 +#: logs/forms.py:41 msgid "Users" msgstr "Utilisateurs" -#: logs/forms.py:41 +#: logs/forms.py:42 msgid "Machines" msgstr "Machines" -#: logs/forms.py:42 +#: logs/forms.py:43 msgid "Subscription" msgstr "Cotisations" -#: logs/forms.py:43 +#: logs/forms.py:44 msgid "Whitelists" msgstr "Accès gracieux" -#: logs/forms.py:44 +#: logs/forms.py:45 msgid "Bans" msgstr "Bannissements" -#: logs/forms.py:45 logs/views.py:437 +#: logs/forms.py:46 logs/views.py:437 msgid "Topology" msgstr "Topologie" -#: logs/forms.py:46 +#: logs/forms.py:47 msgid "All" msgstr "Tous" @@ -66,34 +66,34 @@ msgstr "Tous" msgid "IPv4" msgstr "IPv4" -#: logs/forms.py:51 logs/templates/logs/machine_history.html:36 +#: logs/forms.py:50 logs/templates/logs/machine_history.html:36 msgid "MAC address" msgstr "Adresse MAC" -#: logs/forms.py:118 +#: logs/forms.py:114 msgid "Performed by" msgstr "Effectué(e) par" -#: logs/forms.py:123 +#: logs/forms.py:120 msgid "Action type" msgstr "Type d'action" -#: logs/forms.py:129 logs/forms.py:152 +#: logs/forms.py:126 logs/forms.py:146 #: logs/templates/logs/machine_history.html:37 msgid "Start date" msgstr "Date de début" -#: logs/forms.py:130 logs/forms.py:153 +#: logs/forms.py:127 logs/forms.py:147 #: logs/templates/logs/machine_history.html:38 msgid "End date" msgstr "Date de fin" -#: logs/forms.py:145 logs/templates/logs/search_machine_history.html:38 -#: logs/templates/logs/search_stats_logs.html:36 +#: logs/forms.py:142 logs/templates/logs/search_machine_history.html:38 +#: logs/templates/logs/search_stats_logs.html:35 msgid "Search" msgstr "Rechercher" -#: logs/forms.py:149 +#: logs/forms.py:144 msgid "Search type" msgstr "Type de recherche" @@ -274,8 +274,8 @@ msgstr "Aucun résultat" msgid "Search machine history" msgstr "Rechercher l'historique des machines" -#: logs/templates/logs/search_stats_logs.html:28 -#: logs/templates/logs/search_stats_logs.html:33 +#: logs/templates/logs/search_stats_logs.html:27 +#: logs/templates/logs/search_stats_logs.html:32 msgid "Search events" msgstr "Recherche les évènements" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index b5a5abb0..efc5c09b 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-09 00:47+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-23 16:35+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -143,47 +143,47 @@ msgstr "Services actuels" msgid "Current VLANs" msgstr "VLANs actuels" -#: machines/models.py:76 +#: machines/models.py:77 msgid "Optional." msgstr "Optionnel." -#: machines/models.py:82 +#: machines/models.py:83 msgid "Can view a machine object" msgstr "Peut voir un objet machine" -#: machines/models.py:83 +#: machines/models.py:84 msgid "Can change the user of a machine" msgstr "Peut changer l'utilisateur d'une machine" -#: machines/models.py:85 machines/views.py:220 +#: machines/models.py:86 machines/views.py:220 msgid "machine" msgstr "machine" -#: machines/models.py:86 +#: machines/models.py:87 msgid "machines" msgstr "machines" -#: machines/models.py:110 +#: machines/models.py:111 msgid "You don't have the right to change the machine's user." msgstr "Vous n'avez pas le droit de changer l'utilisateur de la machine." -#: machines/models.py:130 +#: machines/models.py:131 msgid "You don't have the right to view all the machines." msgstr "Vous n'avez pas le droit de voir toutes les machines." -#: machines/models.py:151 +#: machines/models.py:152 msgid "Nonexistent user." msgstr "Utilisateur inexistant." -#: machines/models.py:161 machines/models.py:1594 +#: machines/models.py:162 machines/models.py:1595 msgid "You don't have the right to add a machine." msgstr "Vous n'avez pas le droit d'ajouter une machine." -#: machines/models.py:167 +#: machines/models.py:168 msgid "You don't have the right to add a machine to another user." msgstr "Vous n'avez pas le droit d'ajouter une machine à un autre utilisateur." -#: machines/models.py:174 machines/models.py:1613 +#: machines/models.py:175 machines/models.py:1614 #, python-format msgid "" "You reached the maximum number of interfaces that you are allowed to create " @@ -192,77 +192,77 @@ msgstr "" "Vous avez atteint le nombre maximal d'interfaces que vous pouvez créer vous-" "même (%s)." -#: machines/models.py:199 machines/models.py:1656 +#: machines/models.py:200 machines/models.py:1657 msgid "You don't have the right to edit a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier une machine d'un autre utilisateur." -#: machines/models.py:222 +#: machines/models.py:223 msgid "You don't have the right to delete a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer une machine d'une autre utilisateur." -#: machines/models.py:245 machines/models.py:2153 +#: machines/models.py:246 machines/models.py:2154 msgid "You don't have the right to view other machines than yours." msgstr "Vous n'avez pas le droit de voir d'autres machines que les vôtres." -#: machines/models.py:260 machines/templates/machines/aff_machines.html:39 +#: machines/models.py:261 machines/templates/machines/aff_machines.html:39 msgid "No name" msgstr "Sans nom" -#: machines/models.py:346 +#: machines/models.py:347 msgid "Can view a machine type object" msgstr "Peut voir un objet type de machine" -#: machines/models.py:347 +#: machines/models.py:348 msgid "Can use all machine types" msgstr "Peut utiliser tous les types de machine" -#: machines/models.py:349 +#: machines/models.py:350 msgid "machine type" msgstr "type de machine" -#: machines/models.py:350 +#: machines/models.py:351 msgid "machine types" msgstr "types de machine" -#: machines/models.py:376 machines/models.py:402 machines/models.py:2234 +#: machines/models.py:377 machines/models.py:403 machines/models.py:2235 msgid "You don't have the right to use all machine types." msgstr "Vous n'avez pas le droit d'utiliser tous les types de machine." -#: machines/models.py:441 +#: machines/models.py:442 msgid "Network containing the domain's IPv4 range (optional)." msgstr "Réseau contenant la plage IPv4 du domaine (optionnel)." -#: machines/models.py:446 +#: machines/models.py:447 msgid "Netmask for the domain's IPv4 range." msgstr "Masque de sous-réseau pour la plage IPv4 du domaine." -#: machines/models.py:449 +#: machines/models.py:450 msgid "Enable reverse DNS for IPv4." msgstr "Activer DNS inverse pour IPv4." -#: machines/models.py:456 +#: machines/models.py:457 msgid "Enable reverse DNS for IPv6." msgstr "Activer DNS inverse pour IPv6." -#: machines/models.py:463 +#: machines/models.py:464 msgid "Can view an IP type object" msgstr "Peut voir un objet type d'IP" -#: machines/models.py:464 +#: machines/models.py:465 msgid "Can use all IP types" msgstr "Peut utiliser tous les types d'IP" -#: machines/models.py:466 machines/templates/machines/machine.html:116 +#: machines/models.py:467 machines/templates/machines/machine.html:116 msgid "IP type" msgstr "Type d'IP" -#: machines/models.py:467 +#: machines/models.py:468 msgid "IP types" msgstr "Types d'IP" -#: machines/models.py:583 +#: machines/models.py:584 msgid "" "One or several IP addresses from the range are affected, impossible to " "delete the range." @@ -270,25 +270,25 @@ msgstr "" "Une ou plusieurs adresses IP de la plage sont affectées, impossible de " "supprimer la plage." -#: machines/models.py:644 +#: machines/models.py:645 msgid "Domaine IPv4 start and stop must be valid" msgstr "Les valeurs IPv4 Domaine ip start et stop doivent être valides" -#: machines/models.py:646 +#: machines/models.py:647 msgid "Range end must be after range start..." msgstr "La fin de la plage doit être après le début..." -#: machines/models.py:651 +#: machines/models.py:652 msgid "The range is too large, you can't create a larger one than a /16." msgstr "" "La plage est trop grande, vous ne pouvez pas en créer une plus grande " "qu'un /16." -#: machines/models.py:659 +#: machines/models.py:660 msgid "The specified range is not disjoint from existing ranges." msgstr "La plage renseignée n'est pas disjointe des plages existantes." -#: machines/models.py:672 +#: machines/models.py:673 msgid "" "If you specify a domain network or netmask, it must contain the domain's IP " "range." @@ -296,47 +296,47 @@ msgstr "" "Si vous renseignez un réseau ou masque de sous-réseau, il doit contenir la " "plage IP du domaine." -#: machines/models.py:727 +#: machines/models.py:728 msgid "v4 multicast management." msgstr "gestion de multidiffusion v4." -#: machines/models.py:728 +#: machines/models.py:729 msgid "v6 multicast management." msgstr "gestion de multidiffusion v6." -#: machines/models.py:731 +#: machines/models.py:732 msgid "Can view a VLAN object" msgstr "Peut voir un objet VLAN" -#: machines/models.py:732 machines/templates/machines/machine.html:168 +#: machines/models.py:733 machines/templates/machines/machine.html:168 msgid "VLAN" msgstr "VLAN" -#: machines/models.py:733 +#: machines/models.py:734 msgid "VLANs" msgstr "VLANs" -#: machines/models.py:752 +#: machines/models.py:753 msgid "MAC-address" msgstr "MAC-address" -#: machines/models.py:767 +#: machines/models.py:768 msgid "Can view a NAS device object" msgstr "Peut voir un objet dispositif NAS" -#: machines/models.py:768 machines/templates/machines/machine.html:172 +#: machines/models.py:769 machines/templates/machines/machine.html:172 msgid "NAS device" msgstr "Dispositif NAS" -#: machines/models.py:769 +#: machines/models.py:770 msgid "NAS devices" msgstr "Dispositifs NAS" -#: machines/models.py:794 +#: machines/models.py:795 msgid "Contact email address for the zone." msgstr "Adresse mail de contact pour la zone." -#: machines/models.py:798 +#: machines/models.py:799 msgid "" "Seconds before the secondary DNS have to ask the primary DNS serial to " "detect a modification." @@ -344,7 +344,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande au DNS primaire le serial pour " "détecter une modification." -#: machines/models.py:805 +#: machines/models.py:806 msgid "" "Seconds before the secondary DNS ask the serial again in case of a primary " "DNS timeout." @@ -352,7 +352,7 @@ msgstr "" "Secondes avant que le DNS secondaire demande le serial de nouveau dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:812 +#: machines/models.py:813 msgid "" "Seconds before the secondary DNS stop answering requests in case of primary " "DNS timeout." @@ -360,127 +360,127 @@ msgstr "" "Secondes avant que le DNS secondaire arrête de répondre aux requêtes dans le " "cas d'un délai d'attente du DNS primaire." -#: machines/models.py:817 machines/models.py:1170 +#: machines/models.py:818 machines/models.py:1171 msgid "Time To Live." msgstr "Temps de vie" -#: machines/models.py:821 +#: machines/models.py:822 msgid "Can view an SOA record object" msgstr "Peut voir un objet enregistrement SOA" -#: machines/models.py:822 machines/templates/machines/aff_extension.html:36 +#: machines/models.py:823 machines/templates/machines/aff_extension.html:36 #: machines/templates/machines/machine.html:128 msgid "SOA record" msgstr "Enregistrement SOA" -#: machines/models.py:823 +#: machines/models.py:824 msgid "SOA records" msgstr "Enregistrements SOA" -#: machines/models.py:863 +#: machines/models.py:864 msgid "SOA to edit" msgstr "SOA à modifier" -#: machines/models.py:883 +#: machines/models.py:884 msgid "Zone name, must begin with a dot (.example.org)." msgstr "Nom de zone, doit commencer par un point (.example.org)." -#: machines/models.py:891 +#: machines/models.py:892 msgid "A record associated with the zone." msgstr "Enregistrement A associé à la zone." -#: machines/models.py:897 +#: machines/models.py:898 msgid "AAAA record associated with the zone." msgstr "Enregristrement AAAA associé avec la zone." -#: machines/models.py:901 +#: machines/models.py:902 msgid "Should the zone be signed with DNSSEC." msgstr "La zone doit-elle être signée avec DNSSEC." -#: machines/models.py:906 +#: machines/models.py:907 msgid "Can view an extension object" msgstr "Peut voir un objet extension" -#: machines/models.py:907 +#: machines/models.py:908 msgid "Can use all extensions" msgstr "Peut utiliser toutes les extensions" -#: machines/models.py:909 +#: machines/models.py:910 msgid "DNS extension" msgstr "Extension DNS" -#: machines/models.py:910 +#: machines/models.py:911 msgid "DNS extensions" msgstr "Extensions DNS" -#: machines/models.py:980 +#: machines/models.py:981 msgid "You don't have the right to use all extensions." msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." -#: machines/models.py:1005 +#: machines/models.py:1006 #, fuzzy #| msgid "You don't have the right to use all extensions." msgid "You don't have the right to list all extensions." msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." -#: machines/models.py:1015 +#: machines/models.py:1016 msgid "An extension must begin with a dot." msgstr "Une extension doit commencer par un point." -#: machines/models.py:1035 machines/models.py:1067 machines/models.py:1099 -#: machines/models.py:1129 machines/models.py:1950 +#: machines/models.py:1036 machines/models.py:1068 machines/models.py:1100 +#: machines/models.py:1130 machines/models.py:1951 msgid "Time To Live (TTL)" msgstr "Temps de vie (TTL)" -#: machines/models.py:1039 +#: machines/models.py:1040 msgid "Can view an MX record object" msgstr "Peut voir un objet enregistrement MX" -#: machines/models.py:1040 machines/templates/machines/machine.html:132 +#: machines/models.py:1041 machines/templates/machines/machine.html:132 msgid "MX record" msgstr "Enregistrement MX" -#: machines/models.py:1041 +#: machines/models.py:1042 msgid "MX records" msgstr "Enregistrements MX" -#: machines/models.py:1071 +#: machines/models.py:1072 msgid "Can view an NS record object" msgstr "Peut voir un objet enregistrement NS" -#: machines/models.py:1072 machines/templates/machines/machine.html:136 +#: machines/models.py:1073 machines/templates/machines/machine.html:136 msgid "NS record" msgstr "Enregistrement NS" -#: machines/models.py:1073 +#: machines/models.py:1074 msgid "NS records" msgstr "Enregistrements NS" -#: machines/models.py:1103 +#: machines/models.py:1104 msgid "Can view a TXT record object" msgstr "Peut voir un objet enregistrement TXT" -#: machines/models.py:1104 machines/templates/machines/machine.html:140 +#: machines/models.py:1105 machines/templates/machines/machine.html:140 msgid "TXT record" msgstr "enregistrement TXT" -#: machines/models.py:1105 +#: machines/models.py:1106 msgid "TXT records" msgstr "enregistrements TXT" -#: machines/models.py:1133 +#: machines/models.py:1134 msgid "Can view a DNAME record object" msgstr "Peut voir un objet enregistrement DNAME" -#: machines/models.py:1134 machines/templates/machines/machine.html:144 +#: machines/models.py:1135 machines/templates/machines/machine.html:144 msgid "DNAME record" msgstr "enregistrement DNAME" -#: machines/models.py:1135 +#: machines/models.py:1136 msgid "DNAME records" msgstr "enregistrements DNAME" -#: machines/models.py:1176 +#: machines/models.py:1177 msgid "" "Priority of the target server (positive integer value, the lower it is, the " "more the server will be used if available)." @@ -488,7 +488,7 @@ msgstr "" "Priorité du serveur cible (entier positif, plus il est bas, plus le serveur " "sera utilisé si disponible)." -#: machines/models.py:1185 +#: machines/models.py:1186 msgid "" "Relative weight for records with the same priority (integer value between 0 " "and 65535)." @@ -496,172 +496,172 @@ msgstr "" "Poids relatif des enregistrements avec la même priorité (entier entre 0 et " "65535)." -#: machines/models.py:1190 +#: machines/models.py:1191 msgid "TCP/UDP port." msgstr "Port TCP/UDP." -#: machines/models.py:1193 +#: machines/models.py:1194 msgid "Target server." msgstr "Serveur cible." -#: machines/models.py:1197 +#: machines/models.py:1198 msgid "Can view an SRV record object" msgstr "Peut voir un objet enregistrement SRV" -#: machines/models.py:1198 machines/templates/machines/machine.html:148 +#: machines/models.py:1199 machines/templates/machines/machine.html:148 msgid "SRV record" msgstr "enregistrement SRV" -#: machines/models.py:1199 +#: machines/models.py:1200 msgid "SRV records" msgstr "enregistrements SRV" -#: machines/models.py:1258 +#: machines/models.py:1259 msgid "SSH public key." msgstr "Clé publique SSH." -#: machines/models.py:1261 +#: machines/models.py:1262 msgid "Comment." msgstr "Commentaire." -#: machines/models.py:1287 +#: machines/models.py:1288 msgid "Can view an SSHFP record object" msgstr "Peut voir un objet enregistrement SSHFP" -#: machines/models.py:1288 machines/templates/machines/machine.html:152 +#: machines/models.py:1289 machines/templates/machines/machine.html:152 #: machines/views.py:387 msgid "SSHFP record" msgstr "enregistrement SSHFP" -#: machines/models.py:1289 +#: machines/models.py:1290 msgid "SSHFP records" msgstr "enregistrements SSHFP" -#: machines/models.py:1322 +#: machines/models.py:1323 msgid "Ssh pub key entry is incorrect base64 entry" msgstr "L'entrée Ssh pub key n'est pas une entrée base64 valide" -#: machines/models.py:1352 +#: machines/models.py:1353 msgid "Can view an interface object" msgstr "Peut voir un objet interface" -#: machines/models.py:1353 +#: machines/models.py:1354 msgid "Can change the owner of an interface" msgstr "Peut changer l'utilisateur d'une interface" -#: machines/models.py:1355 machines/views.py:271 +#: machines/models.py:1356 machines/views.py:271 msgid "interface" msgstr "interface" -#: machines/models.py:1356 +#: machines/models.py:1357 msgid "interfaces" msgstr "interfaces" -#: machines/models.py:1399 +#: machines/models.py:1400 msgid "Unknown vendor." msgstr "Constructeur inconnu." -#: machines/models.py:1469 +#: machines/models.py:1470 msgid "The given MAC address is invalid." msgstr "L'adresse MAC indiquée est invalide." -#: machines/models.py:1478 +#: machines/models.py:1479 msgid "There are no IP addresses available in the slash." msgstr "Il n'y a pas d'adresses IP disponibles dans le slash." -#: machines/models.py:1543 +#: machines/models.py:1544 msgid "The selected IP type is invalid." msgstr "Le type d'IP sélectionné est invalide." -#: machines/models.py:1557 +#: machines/models.py:1558 msgid "MAC address already registered in this machine type/subnet." msgstr "Adresse MAC déjà enregistrée dans ce type de machine/sous-réseau." -#: machines/models.py:1566 +#: machines/models.py:1567 msgid "The IPv4 address and the machine type don't match." msgstr "L'adresse IPv4 et le type de machine ne correspondent pas." -#: machines/models.py:1587 +#: machines/models.py:1588 msgid "Nonexistent machine." msgstr "Machine inexistante." -#: machines/models.py:1604 +#: machines/models.py:1605 msgid "" "You don't have the right to add an interface to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter une interface à une machine d'un autre " "utilisateur." -#: machines/models.py:1634 +#: machines/models.py:1635 msgid "You don't have the right to edit the machine." msgstr "Vous n'avez pas le droit d'éditer une machine." -#: machines/models.py:1680 +#: machines/models.py:1681 msgid "You don't have the right to delete interfaces of another user." msgstr "" "Vous n'avez pas le droit de supprimer une interface d'une autre utilisateur." -#: machines/models.py:1704 +#: machines/models.py:1705 msgid "You don't have the right to view interfaces other than yours." msgstr "Vous n'avez pas le droit de voir d'autres interfaces que les vôtres." -#: machines/models.py:1738 +#: machines/models.py:1739 msgid "If false,the DNS will not provide this ip." msgstr "Si faux, le DNS n'annoncera pas cette ip." -#: machines/models.py:1743 +#: machines/models.py:1744 msgid "Can view an IPv6 addresses list object" msgstr "Peut voir un objet list d'adresses IPv6" -#: machines/models.py:1746 +#: machines/models.py:1747 msgid "Can change the SLAAC value of an IPv6 addresses list" msgstr "Peut modifier la valeur SLAAC d'une liste d'adresses IPv6" -#: machines/models.py:1749 machines/views.py:331 +#: machines/models.py:1750 machines/views.py:331 msgid "IPv6 addresses list" msgstr "Liste d'adresses IPv6" -#: machines/models.py:1750 +#: machines/models.py:1751 msgid "IPv6 addresses lists" msgstr "Listes d'adresses IPv6" -#: machines/models.py:1768 machines/models.py:2053 +#: machines/models.py:1769 machines/models.py:2054 msgid "Nonexistent interface." msgstr "Interface inexistante." -#: machines/models.py:1774 +#: machines/models.py:1775 msgid "You don't have the right to add ipv6 to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter des ipv6 à une machine d'un autre " "utilisateur." -#: machines/models.py:1787 +#: machines/models.py:1788 msgid "You don't have the right to change the SLAAC value of an IPv6 address." msgstr "" "Vous n'avez pas le droit de changer la valeur SLAAC d'une adresse IPv6." -#: machines/models.py:1812 +#: machines/models.py:1813 msgid "You don't have the right to edit ipv6 of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier les ipv6 d'une machine d'un autre " "utilisateur." -#: machines/models.py:1837 +#: machines/models.py:1838 msgid "You don't have the right to delete ipv6 of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer les ipv6 d'une machine d'une autre " "utilisateur." -#: machines/models.py:1861 +#: machines/models.py:1862 msgid "You don't have the right to view ipv6 of machines other than yours." msgstr "" "Vous n'avez pas le droit de voir les ipv6 d'autres machines que les vôtres." -#: machines/models.py:1894 +#: machines/models.py:1895 msgid "A SLAAC IP address is already registered." msgstr "Une adresse IP SLAAC est déjà enregistrée." -#: machines/models.py:1908 +#: machines/models.py:1909 msgid "" "The v6 prefix is incorrect and doesn't match the type associated with the " "machine." @@ -669,55 +669,55 @@ msgstr "" "Le préfixe v6 est incorrect et ne correspond pas au type associé à la " "machine." -#: machines/models.py:1943 +#: machines/models.py:1944 msgid "Mandatory and unique, must not contain dots." msgstr "Obligatoire et unique, ne doit pas contenir de points." -#: machines/models.py:1958 +#: machines/models.py:1959 msgid "Can view a domain object" msgstr "Peut voir un objet domaine" -#: machines/models.py:1959 +#: machines/models.py:1960 msgid "Can change the TTL of a domain object" msgstr "Peut changer le TTL d'un objet domaine" -#: machines/models.py:1961 +#: machines/models.py:1962 msgid "domain" msgstr "domaine" -#: machines/models.py:1962 +#: machines/models.py:1963 msgid "domains" msgstr "domaines" -#: machines/models.py:1989 +#: machines/models.py:1990 msgid "You can't create a both A and CNAME record." msgstr "Vous ne pouvez pas créer un enregistrement à la fois A et CNAME." -#: machines/models.py:1992 +#: machines/models.py:1993 msgid "You can't create a CNAME record pointing to itself." msgstr "Vous ne pouvez pas créer un enregistrement CNAME vers lui-même." -#: machines/models.py:1998 +#: machines/models.py:1999 #, python-format msgid "The domain name %s is too long (over 63 characters)." msgstr "Le nom de domaine %s est trop long (plus de 63 caractères)." -#: machines/models.py:2002 +#: machines/models.py:2003 #, python-format msgid "The domain name %s contains forbidden characters." msgstr "Le nom de domaine %s contient des caractères interdits." -#: machines/models.py:2020 +#: machines/models.py:2021 msgid "Invalid extension." msgstr "Extension invalide." -#: machines/models.py:2062 +#: machines/models.py:2063 msgid "You don't have the right to add an alias to a machine of another user." msgstr "" "Vous n'avez pas le droit d'ajouter un alias à une machine d'un autre " "utilisateur." -#: machines/models.py:2078 +#: machines/models.py:2079 #, python-format msgid "" "You reached the maximum number of alias that you are allowed to create " @@ -726,164 +726,164 @@ msgstr "" "Vous avez atteint le nombre maximal d'alias que vous pouvez créer vous-même " "(%s)." -#: machines/models.py:2104 +#: machines/models.py:2105 msgid "You don't have the right to edit an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de modifier un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2129 +#: machines/models.py:2130 msgid "" "You don't have the right to delete an alias of a machine of another user." msgstr "" "Vous n'avez pas le droit de supprimer un alias d'une machine d'un autre " "utilisateur." -#: machines/models.py:2164 +#: machines/models.py:2165 msgid "You don't have the right to change the domain's TTL." msgstr "Vous n'avez pas le droit de changer le TTL du domaine." -#: machines/models.py:2186 +#: machines/models.py:2187 msgid "Can view an IPv4 addresses list object" msgstr "Peut voir un object liste d'adresses IPv4" -#: machines/models.py:2187 +#: machines/models.py:2188 msgid "IPv4 addresses list" msgstr "Liste d'adresses IPv4" -#: machines/models.py:2188 +#: machines/models.py:2189 msgid "IPv4 addresses lists" msgstr "Listes d'adresses IPv4" -#: machines/models.py:2205 +#: machines/models.py:2206 msgid "The IPv4 address and the range of the IP type don't match." msgstr "L'adresse IPv4 et la plage du type d'IP ne correspondent pas." -#: machines/models.py:2257 +#: machines/models.py:2258 msgid "DHCP server" msgstr "Serveur DHCP" -#: machines/models.py:2258 +#: machines/models.py:2259 msgid "Switches configuration server" msgstr "Serveur de configuration des commutateurs réseau" -#: machines/models.py:2259 +#: machines/models.py:2260 msgid "Recursive DNS server" msgstr "Serveur DNS récursif" -#: machines/models.py:2260 +#: machines/models.py:2261 msgid "NTP server" msgstr "Serveur NTP" -#: machines/models.py:2261 +#: machines/models.py:2262 msgid "RADIUS server" msgstr "Serveur RADIUS" -#: machines/models.py:2262 +#: machines/models.py:2263 msgid "Log server" msgstr "Serveur log" -#: machines/models.py:2263 +#: machines/models.py:2264 msgid "LDAP master server" msgstr "Serveur LDAP maître" -#: machines/models.py:2264 +#: machines/models.py:2265 msgid "LDAP backup server" msgstr "Serveur LDAP de secours" -#: machines/models.py:2265 +#: machines/models.py:2266 msgid "SMTP server" msgstr "Serveur SMTP" -#: machines/models.py:2266 +#: machines/models.py:2267 msgid "postgreSQL server" msgstr "Serveur postgreSQL" -#: machines/models.py:2267 +#: machines/models.py:2268 msgid "mySQL server" msgstr "Serveur mySQL" -#: machines/models.py:2268 +#: machines/models.py:2269 msgid "SQL client" msgstr "Client SQL" -#: machines/models.py:2269 +#: machines/models.py:2270 msgid "Gateway" msgstr "Passerelle" -#: machines/models.py:2277 +#: machines/models.py:2278 msgid "Can view a role object" msgstr "Peut voir un objet rôle" -#: machines/models.py:2278 +#: machines/models.py:2279 msgid "server role" msgstr "rôle de serveur" -#: machines/models.py:2279 +#: machines/models.py:2280 msgid "server roles" msgstr "rôles de serveur" -#: machines/models.py:2318 +#: machines/models.py:2319 msgid "Minimal time before regeneration of the service." msgstr "Temps minimal avant régénération du service." -#: machines/models.py:2322 +#: machines/models.py:2323 msgid "Maximal time before regeneration of the service." msgstr "Temps maximal avant régénération du service." -#: machines/models.py:2327 +#: machines/models.py:2328 msgid "Can view a service object" msgstr "Peut voir un objet service" -#: machines/models.py:2328 +#: machines/models.py:2329 msgid "service to generate (DHCP, DNS, ...)" msgstr "service à générer (DHCP, DNS, ...)" -#: machines/models.py:2329 +#: machines/models.py:2330 msgid "services to generate (DHCP, DNS, ...)" msgstr "services à générer (DHCP, DNS, ...)" -#: machines/models.py:2386 +#: machines/models.py:2387 msgid "Can view a service server link object" msgstr "Peut voir un objet lien service serveur" -#: machines/models.py:2388 +#: machines/models.py:2389 msgid "link between service and server" msgstr "lien entre service et serveur" -#: machines/models.py:2389 +#: machines/models.py:2390 msgid "links between service and server" msgstr "liens entre service et serveur" -#: machines/models.py:2436 +#: machines/models.py:2437 msgid "Name of the ports configuration" msgstr "Nom de la configuration de ports" -#: machines/models.py:2441 +#: machines/models.py:2442 msgid "Can view a ports opening list object" msgstr "Peut voir un objet liste d'ouverture de ports" -#: machines/models.py:2443 +#: machines/models.py:2444 msgid "ports opening list" msgstr "liste d'ouverture de ports" -#: machines/models.py:2444 +#: machines/models.py:2445 msgid "ports opening lists" msgstr "listes d'ouverture de ports" -#: machines/models.py:2459 +#: machines/models.py:2460 msgid "You don't have the right to delete a ports opening list." msgstr "Vous n'avez pas le droit de supprimer une liste d'ouverture de ports." -#: machines/models.py:2463 +#: machines/models.py:2464 msgid "This ports opening list is used." msgstr "Cette liste d'ouverture de ports est utilisée." -#: machines/models.py:2527 +#: machines/models.py:2528 msgid "ports opening" msgstr "ouverture de ports" -#: machines/models.py:2528 +#: machines/models.py:2529 msgid "ports openings" msgstr "ouvertures de ports" diff --git a/multi_op/locale/fr/LC_MESSAGES/django.po b/multi_op/locale/fr/LC_MESSAGES/django.po index efed595a..903e563d 100644 --- a/multi_op/locale/fr/LC_MESSAGES/django.po +++ b/multi_op/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2019-11-16 00:22+0100\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" diff --git a/preferences/locale/fr/LC_MESSAGES/django.po b/preferences/locale/fr/LC_MESSAGES/django.po index 74e962b8..5cd147c3 100644 --- a/preferences/locale/fr/LC_MESSAGES/django.po +++ b/preferences/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-24 15:54+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -34,261 +34,261 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: preferences/forms.py:65 +#: preferences/forms.py:69 #: preferences/templates/preferences/display_preferences.html:150 msgid "Telephone number required" msgstr "Numéro de téléphone requis" -#: preferences/forms.py:66 +#: preferences/forms.py:70 msgid "GPG fingerprint" msgstr "Empreinte GPG" -#: preferences/forms.py:67 +#: preferences/forms.py:71 msgid "All can create a club" msgstr "Tous peuvent créer un club" -#: preferences/forms.py:68 +#: preferences/forms.py:72 msgid "All can create a member" msgstr "Tous peuvent créer un adhérent" -#: preferences/forms.py:69 +#: preferences/forms.py:73 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: preferences/forms.py:70 +#: preferences/forms.py:74 msgid "Self change shell" msgstr "Automodification du shell" -#: preferences/forms.py:71 +#: preferences/forms.py:75 msgid "Self change pseudo" msgstr "Automodification du pseudo" -#: preferences/forms.py:72 +#: preferences/forms.py:76 msgid "Self room policy" msgstr "Automodification de la chambre" -#: preferences/forms.py:73 +#: preferences/forms.py:77 #: preferences/templates/preferences/display_preferences.html:85 msgid "Local email accounts enabled" msgstr "Comptes mail locaux activés" -#: preferences/forms.py:74 +#: preferences/forms.py:78 #: preferences/templates/preferences/display_preferences.html:87 #: preferences/templates/preferences/display_preferences.html:167 msgid "Local email domain" msgstr "Domaine de mail local" -#: preferences/forms.py:75 +#: preferences/forms.py:79 msgid "Max local email address" msgstr "Maximum de mail local autorisés" -#: preferences/forms.py:76 +#: preferences/forms.py:80 msgid "Delete not yet active users" msgstr "Suppression des utilisateurs n'ayant jamais adhéré après" -#: preferences/forms.py:77 +#: preferences/forms.py:81 msgid "Disabled email not yet confirmed" msgstr "Désactivation sans confirmation de l'adresse mail" -#: preferences/forms.py:78 +#: preferences/forms.py:82 #: preferences/templates/preferences/display_preferences.html:122 msgid "Self registration" msgstr "Autoinscription" -#: preferences/forms.py:79 +#: preferences/forms.py:83 msgid "All users are state active by default" msgstr "Tous les utilisateurs sont actifs par défault" -#: preferences/forms.py:80 +#: preferences/forms.py:84 msgid "Allow set password during user creation" msgstr "" "Permettre le choix d'un mot de passe directement lors de la création du " "compte" -#: preferences/forms.py:81 +#: preferences/forms.py:85 msgid "Allow archived connexion" msgstr "Autoriser les utilisateurs archivés à se connecter" -#: preferences/forms.py:95 +#: preferences/forms.py:99 msgid "Possibility to set a password per machine" msgstr "Possibilité de mettre un mot de passe par machine" -#: preferences/forms.py:98 +#: preferences/forms.py:102 #: preferences/templates/preferences/display_preferences.html:197 msgid "Maximum number of interfaces allowed for a standard user" msgstr "Nombre maximum d'interfaces autorisé pour un utilisateur standard" -#: preferences/forms.py:101 +#: preferences/forms.py:105 #: preferences/templates/preferences/display_preferences.html:201 msgid "Maximum number of DNS aliases allowed for a standard user" msgstr "Nombre maximum d'alias DNS autorisé pour un utilisateur standard" -#: preferences/forms.py:103 +#: preferences/forms.py:107 msgid "IPv6 mode" msgstr "Mode IPv6" -#: preferences/forms.py:104 +#: preferences/forms.py:108 msgid "Can create a machine" msgstr "Peut créer une machine" -#: preferences/forms.py:145 +#: preferences/forms.py:156 msgid "General message in French" msgstr "Message général en français" -#: preferences/forms.py:146 +#: preferences/forms.py:157 msgid "General message in English" msgstr "Message général en anglais" -#: preferences/forms.py:148 +#: preferences/forms.py:159 #: preferences/templates/preferences/display_preferences.html:59 msgid "Number of results displayed when searching" msgstr "Nombre de résultats affichés lors de la recherche" -#: preferences/forms.py:151 +#: preferences/forms.py:162 msgid "Number of items per page, standard size (e.g. users)" msgstr "Nombre d'éléments par page, taille standard (ex : utilisateurs)" -#: preferences/forms.py:154 +#: preferences/forms.py:165 msgid "Number of items per page, large size (e.g. machines)" msgstr "Nombre d'éléments par page, taille importante (ex : machines)" -#: preferences/forms.py:157 +#: preferences/forms.py:168 #: preferences/templates/preferences/display_preferences.html:67 msgid "Time before expiration of the reset password link (in hours)" msgstr "" "Temps avant expiration du lien de réinitialisation de mot de passe (en " "heures)" -#: preferences/forms.py:159 +#: preferences/forms.py:170 #: preferences/templates/preferences/display_preferences.html:53 msgid "Website name" msgstr "Nom du site" -#: preferences/forms.py:160 +#: preferences/forms.py:171 #: preferences/templates/preferences/display_preferences.html:55 msgid "Email address for automatic emailing" msgstr "Adresse mail pour les mails automatiques" -#: preferences/forms.py:161 +#: preferences/forms.py:172 #: preferences/templates/preferences/display_preferences.html:77 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: preferences/forms.py:162 +#: preferences/forms.py:173 #: preferences/templates/preferences/display_preferences.html:79 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: preferences/forms.py:175 +#: preferences/forms.py:191 msgid "Organisation name" msgstr "Nom de l'association" -#: preferences/forms.py:176 +#: preferences/forms.py:192 #: preferences/templates/preferences/display_preferences.html:330 msgid "SIRET number" msgstr "Numéro SIRET" -#: preferences/forms.py:177 +#: preferences/forms.py:193 msgid "Address (line 1)" msgstr "Adresse (ligne 1)" -#: preferences/forms.py:178 +#: preferences/forms.py:194 msgid "Address (line 2)" msgstr "Adresse (ligne 2)" -#: preferences/forms.py:179 +#: preferences/forms.py:195 #: preferences/templates/preferences/display_preferences.html:338 msgid "Contact email address" msgstr "Adresse mail de contact" -#: preferences/forms.py:180 +#: preferences/forms.py:196 #: preferences/templates/preferences/display_preferences.html:342 msgid "Telephone number" msgstr "Numéro de téléphone" -#: preferences/forms.py:181 +#: preferences/forms.py:197 #: preferences/templates/preferences/display_preferences.html:344 msgid "Usual name" msgstr "Nom d'usage" -#: preferences/forms.py:183 +#: preferences/forms.py:199 msgid "Account used for editing from /admin" msgstr "Compte utilisé pour les modifications depuis /admin" -#: preferences/forms.py:185 preferences/forms.py:332 +#: preferences/forms.py:201 preferences/forms.py:353 msgid "Description" msgstr "Description" -#: preferences/forms.py:199 +#: preferences/forms.py:215 msgid "Message for the French welcome email" msgstr "Message pour le mail de bienvenue en français" -#: preferences/forms.py:202 +#: preferences/forms.py:218 msgid "Message for the English welcome email" msgstr "Message pour le mail de bienvenue en anglais" -#: preferences/forms.py:218 +#: preferences/forms.py:234 msgid "Facebook URL" msgstr "URL du compte Facebook" -#: preferences/forms.py:219 +#: preferences/forms.py:235 msgid "Twitter URL" msgstr "URL du compte Twitter" -#: preferences/forms.py:220 +#: preferences/forms.py:236 #: preferences/templates/preferences/display_preferences.html:505 msgid "Twitter account name" msgstr "Nom du compte Twitter" -#: preferences/forms.py:238 +#: preferences/forms.py:254 msgid "You chose to set vlan but did not set any VLAN." msgstr "" "Vous avez choisi de paramétrer vlan mais vous n'avez indiqué aucun VLAN." -#: preferences/forms.py:239 +#: preferences/forms.py:255 msgid "Please, choose a VLAN." msgstr "Veuillez choisir un VLAN." -#: preferences/forms.py:270 +#: preferences/forms.py:291 msgid "There is already a mandate taking place at the specified start date." msgstr "Il y a déjà un mandat ayant cours à la date de début renseignée." -#: preferences/forms.py:284 +#: preferences/forms.py:305 msgid "There is already a mandate taking place at the specified end date." msgstr "Il y a déjà un madant ayant cours à la date de fin renseignée." -#: preferences/forms.py:298 +#: preferences/forms.py:319 msgid "The specified dates overlap with an existing mandate." msgstr "Les dates renseignées se superposent avec un mandat existant." -#: preferences/forms.py:330 +#: preferences/forms.py:351 #: preferences/templates/preferences/display_preferences.html:328 msgid "Name" msgstr "Nom" -#: preferences/forms.py:331 +#: preferences/forms.py:352 #: preferences/templates/preferences/aff_service.html:55 msgid "URL" msgstr "URL" -#: preferences/forms.py:333 +#: preferences/forms.py:354 #: preferences/templates/preferences/aff_service.html:58 msgid "Image" msgstr "Image" -#: preferences/forms.py:342 +#: preferences/forms.py:363 msgid "Current services" msgstr "Services actuels" -#: preferences/forms.py:430 +#: preferences/forms.py:457 msgid "Current email addresses" msgstr "Adresses mail actuelles" -#: preferences/forms.py:460 +#: preferences/forms.py:487 msgid "Current document templates" msgstr "Modèles de document actuels" -#: preferences/forms.py:490 +#: preferences/forms.py:517 msgid "Current attributes" msgstr "Attributs actuels" @@ -481,7 +481,7 @@ msgstr "Clé par défaut pour les commutateurs réseau." msgid "Can view a RADIUS key object" msgstr "Peut voir un objet clé RADIUS" -#: preferences/models.py:457 preferences/views.py:334 +#: preferences/models.py:457 preferences/views.py:335 msgid "RADIUS key" msgstr "Clé RADIUS" @@ -514,7 +514,7 @@ msgstr "Identifiants par défaut pour les commutateurs réseau." msgid "Can view a switch management credentials object" msgstr "Peut voir un objet identifiants de gestion de commutateur réseau" -#: preferences/models.py:492 preferences/views.py:396 +#: preferences/models.py:492 preferences/views.py:400 msgid "switch management credentials" msgstr "identifiants de gestion de commutateur réseau" @@ -534,7 +534,7 @@ msgstr "Message affiché spécifiquement pour ce rappel." msgid "Can view a reminder object" msgstr "Peut voir un objet rappel" -#: preferences/models.py:522 preferences/views.py:279 +#: preferences/models.py:522 preferences/views.py:280 msgid "reminder" msgstr "rappel" @@ -570,7 +570,7 @@ msgstr "préférences générales" msgid "Can view the service preferences" msgstr "Peut voir les préférences de service" -#: preferences/models.py:613 preferences/views.py:230 +#: preferences/models.py:613 preferences/views.py:231 msgid "service" msgstr "service" @@ -598,7 +598,7 @@ msgstr "adresse mail de contact" msgid "contact email addresses" msgstr "adresses mail de contact" -#: preferences/models.py:665 preferences/views.py:628 +#: preferences/models.py:665 preferences/views.py:635 msgid "mandate" msgstr "mandat" @@ -690,7 +690,7 @@ msgstr "attribut RADIUS" msgid "RADIUS attributes" msgstr "attributs RADIUS" -#: preferences/models.py:817 preferences/views.py:581 +#: preferences/models.py:817 preferences/views.py:588 msgid "attribute" msgstr "attribut" @@ -1058,8 +1058,8 @@ msgstr "Confirmer" #: preferences/templates/preferences/display_preferences.html:31 #: preferences/templates/preferences/display_preferences.html:34 -#: preferences/templates/preferences/edit_preferences.html:30 -#: preferences/templates/preferences/preferences.html:30 +#: preferences/templates/preferences/edit_preferences.html:29 +#: preferences/templates/preferences/preferences.html:29 msgid "Preferences" msgstr "Préférences" @@ -1076,9 +1076,9 @@ msgstr "Préférences générales" #: preferences/templates/preferences/display_preferences.html:420 #: preferences/templates/preferences/display_preferences.html:498 #: preferences/templates/preferences/edit_preferences.html:46 -#: preferences/views.py:215 preferences/views.py:264 preferences/views.py:310 -#: preferences/views.py:367 preferences/views.py:431 preferences/views.py:492 -#: preferences/views.py:566 preferences/views.py:613 +#: preferences/views.py:216 preferences/views.py:265 preferences/views.py:311 +#: preferences/views.py:371 preferences/views.py:438 preferences/views.py:499 +#: preferences/views.py:573 preferences/views.py:620 msgid "Edit" msgstr "Modifier" @@ -1407,67 +1407,67 @@ msgstr "Objet inconnu." msgid "The preferences were edited." msgstr "Les préférences ont été modifiées." -#: preferences/views.py:194 +#: preferences/views.py:195 msgid "The service was added." msgstr "Le service a été ajouté." -#: preferences/views.py:197 preferences/views.py:246 preferences/views.py:295 -#: preferences/views.py:350 preferences/views.py:413 preferences/views.py:469 -#: preferences/views.py:548 preferences/views.py:597 +#: preferences/views.py:198 preferences/views.py:247 preferences/views.py:296 +#: preferences/views.py:353 preferences/views.py:419 preferences/views.py:476 +#: preferences/views.py:555 preferences/views.py:604 msgid "Add" msgstr "Ajouter" -#: preferences/views.py:212 +#: preferences/views.py:213 msgid "The service was edited." msgstr "Le service a été modifié." -#: preferences/views.py:227 +#: preferences/views.py:228 msgid "The service was deleted." msgstr "Le service a été supprimé." -#: preferences/views.py:243 +#: preferences/views.py:244 msgid "The reminder was added." msgstr "Le rappel a été ajouté." -#: preferences/views.py:261 +#: preferences/views.py:262 msgid "The reminder was edited." msgstr "Le rappel a été modifié." -#: preferences/views.py:276 +#: preferences/views.py:277 msgid "The reminder was deleted." msgstr "Le rappel a été supprimé." -#: preferences/views.py:292 +#: preferences/views.py:293 msgid "The RADIUS key was added." msgstr "La clé RADIUS a été ajoutée." -#: preferences/views.py:307 +#: preferences/views.py:308 msgid "The RADIUS key was edited." msgstr "La clé RADIUS a été modifiée." -#: preferences/views.py:323 +#: preferences/views.py:324 msgid "The RADIUS key was deleted." msgstr "La clé RADIUS a été supprimée." -#: preferences/views.py:328 +#: preferences/views.py:329 msgid "The RADIUS key is assigned to at least one switch, you can't delete it." msgstr "" "La clé RADIUS est assignée a au moins un commutateur réseau, vous ne pouvez " "pas la supprimer." -#: preferences/views.py:347 +#: preferences/views.py:348 msgid "The switch management credentials were added." msgstr "Les identifiants de gestion de commutateur réseay ont été ajoutés." -#: preferences/views.py:364 +#: preferences/views.py:368 msgid "The switch management credentials were edited." msgstr "Les identifiants de gestion de commutateur réseau ont été modifiés." -#: preferences/views.py:381 +#: preferences/views.py:385 msgid "The switch management credentials were deleted." msgstr "Les identifiants de gestion de commutateur réseau ont été supprimés." -#: preferences/views.py:387 +#: preferences/views.py:391 msgid "" "The switch management credentials are assigned to at least one switch, you " "can't delete them." @@ -1475,44 +1475,44 @@ msgstr "" "Les identifiants de gestion de commutateur réseau sont assignés à au moins " "un commutateur réseau , vous ne pouvez pas les supprimer." -#: preferences/views.py:410 +#: preferences/views.py:414 msgid "The contact email address was created." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:428 +#: preferences/views.py:435 msgid "The contact email address was edited." msgstr "L'adresse mail de contact a été modifiée." -#: preferences/views.py:446 +#: preferences/views.py:453 msgid "The contact email adress was deleted." msgstr "L'adresse mail de contact a été supprimée." -#: preferences/views.py:449 preferences/views.py:530 +#: preferences/views.py:456 preferences/views.py:537 msgid "Delete" msgstr "Supprimer" -#: preferences/views.py:464 +#: preferences/views.py:471 msgid "The document template was created." msgstr "Le modèle de document a été créé." -#: preferences/views.py:470 +#: preferences/views.py:477 msgid "New document template" msgstr "Nouveau modèle de document" -#: preferences/views.py:487 +#: preferences/views.py:494 msgid "The document template was edited." msgstr "Le modèle de document a été édité." -#: preferences/views.py:493 +#: preferences/views.py:500 msgid "Edit document template" msgstr "Modifier le modèle de document" -#: preferences/views.py:514 +#: preferences/views.py:521 #, python-format msgid "The document template %(document_template)s was deleted." msgstr "Le modèle de document %(document_template)s a été supprimé." -#: preferences/views.py:521 +#: preferences/views.py:528 #, python-format msgid "" "The document template %(document_template)s can't be deleted because it is " @@ -1521,31 +1521,31 @@ msgstr "" "Le modèle de document %(document_template)s ne peut pas être supprimé car il " "est actuellement utilisé." -#: preferences/views.py:531 +#: preferences/views.py:538 msgid "Delete document template" msgstr "Supprimer le modèle de document" -#: preferences/views.py:545 +#: preferences/views.py:552 msgid "The attribute was added." msgstr "L'attribut a été ajouté." -#: preferences/views.py:563 +#: preferences/views.py:570 msgid "The attribute was edited." msgstr "L'attribut a été modifié." -#: preferences/views.py:578 +#: preferences/views.py:585 msgid "The attribute was deleted." msgstr "L'attribut a été supprimé." -#: preferences/views.py:594 +#: preferences/views.py:601 msgid "The mandate was added." msgstr "Le mandat a été ajouté." -#: preferences/views.py:610 +#: preferences/views.py:617 msgid "The mandate was edited." msgstr "Le mandat a été modifié." -#: preferences/views.py:625 +#: preferences/views.py:632 msgid "The mandate was deleted." msgstr "Le mandat été supprimé." diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index a734d26e..ac72912b 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -30,29 +30,29 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: re2o/acl.py:47 +#: re2o/acl.py:48 msgid "You don't have the right to edit this option." msgstr "Vous n'avez pas le droit de modifier cette option." -#: re2o/acl.py:50 +#: re2o/acl.py:51 #, python-format msgid "You need to be a member of one of these groups: %s." msgstr "Vous devez être membre de l'un de ces groupes : %s." -#: re2o/acl.py:53 +#: re2o/acl.py:54 #, python-format msgid "No group has the %s permission(s)!" msgstr "Aucun groupe ne possède la ou les permissions %s !" -#: re2o/acl.py:194 +#: re2o/acl.py:203 msgid "Nonexistent entry." msgstr "Entrée inexistante." -#: re2o/acl.py:250 re2o/acl.py:322 +#: re2o/acl.py:261 re2o/acl.py:342 msgid "You don't have the right to access this menu." msgstr "Vous n'avez pas le droit d'accéder à ce menu." -#: re2o/acl.py:372 +#: re2o/acl.py:399 msgid "You don't have the right to edit the history." msgstr "Vous n'avez pas le droit de modifier l'historique." @@ -79,36 +79,48 @@ msgstr "Format : {main}" msgid "Failed to send email: %(error)s." msgstr "Échec de l'envoi du mail : %(error)s." -#: re2o/mixins.py:121 +#: re2o/mixins.py:123 #, python-format msgid "You don't have the right to create a %s object." msgstr "Vous n'avez pas le droit de créer un objet %s." -#: re2o/mixins.py:142 +#: re2o/mixins.py:144 #, python-format msgid "You don't have the right to edit a %s object." msgstr "Vous n'avez pas le droit de modifier un objet %s." -#: re2o/mixins.py:163 +#: re2o/mixins.py:165 #, python-format msgid "You don't have the right to delete a %s object." msgstr "Vous n'avez pas le droit de supprimer un objet %s." -#: re2o/mixins.py:184 +#: re2o/mixins.py:186 #, python-format msgid "You don't have the right to view every %s object." msgstr "Vous n'avez pas le droit de voir tous les objets %s." -#: re2o/mixins.py:205 +#: re2o/mixins.py:207 +#, fuzzy, python-format +#| msgid "You don't have the right to view every %s object." +msgid "You don't have the right to edit every %s object." +msgstr "Vous n'avez pas le droit de voir tous les objets %s." + +#: re2o/mixins.py:228 +#, fuzzy, python-format +#| msgid "You don't have the right to view every %s object." +msgid "You don't have the right to list every %s object." +msgstr "Vous n'avez pas le droit de voir tous les objets %s." + +#: re2o/mixins.py:250 #, python-format msgid "You don't have the right to view a %s object." msgstr "Vous n'avez pas le droit de voir un objet %s." -#: re2o/settings.py:154 +#: re2o/settings.py:156 msgid "English" msgstr "Anglais" -#: re2o/settings.py:154 +#: re2o/settings.py:156 msgid "French" msgstr "Français" @@ -161,8 +173,8 @@ msgid "" "process, we will be glad to welcome you so do not hesitate to contact us and " "come help us build the future of Re2o." msgstr "" -"Re2o est un outil d'administration initié par Rézo Metz et quelques membres d'autres associations de Rézo Metz et quelques membres d'autres associations de FedeRez autour de l'été 2016.
    Il se veut " "être un outil indépendant de toute infrastructure réseau pour pouvoir être " "installé en « quelques étapes ». Cet outil est entièrement gratuit et est " @@ -328,14 +340,13 @@ msgstr "Tweets de @%(twitter_account_name)s" msgid "Follow @%(twitter_account_name)s" msgstr "Suivre @%(twitter_account_name)s" -#: re2o/templatetags/massive_bootstrap_form.py:413 -msgid "Nothing" -msgstr "Rien" - #: re2o/urls.py:57 msgid "Homepage" msgstr "Page d'accueil" -#: re2o/views.py:110 re2o/views.py:121 +#: re2o/views.py:112 re2o/views.py:123 msgid "Unable to get the information." msgstr "Impossible d'obtenir l'information." + +#~ msgid "Nothing" +#~ msgstr "Rien" diff --git a/search/locale/fr/LC_MESSAGES/django.po b/search/locale/fr/LC_MESSAGES/django.po index 353e0ad6..80f8ef0b 100644 --- a/search/locale/fr/LC_MESSAGES/django.po +++ b/search/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-24 20:10+0200\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 5c6c4af0..4909dd5d 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -80,7 +80,7 @@ msgstr "Non disponible" msgid "Unknown content" msgstr "Contenu incconu" -#: templates/base.html:79 templates/registration/logged_out.html:11 +#: templates/base.html:76 templates/registration/logged_out.html:11 #: templates/registration/password_change_done.html:11 #: templates/registration/password_change_form.html:11 #: templates/registration/password_reset_complete.html:11 @@ -452,7 +452,8 @@ msgstr "Dernière page" #: templates/registration/logged_out.html:16 msgid "Thanks for spending some quality time with the Web site today." -msgstr "Merci pour avoir passé un moment de qualite sur notre site aujourd'hui." +msgstr "" +"Merci pour avoir passé un moment de qualite sur notre site aujourd'hui." #: templates/registration/logged_out.html:17 msgid "Log in again" @@ -483,12 +484,14 @@ msgid "" "You're receiving this email because you requested a password reset for your " "user account at %(site_name)s." msgstr "" -"Vous recevez cet email car vous avez demandé une réinitialisation du mot de passe " -"de votre compte sur %(site_name)s." +"Vous recevez cet email car vous avez demandé une réinitialisation du mot de " +"passe de votre compte sur %(site_name)s." #: templates/registration/password_reset_email.html:4 msgid "Please go to the following page and choose a new password:" -msgstr "Merci de vous rendre sur la page suivante et de choisir un nouveau mot de passe : " +msgstr "" +"Merci de vous rendre sur la page suivante et de choisir un nouveau mot de " +"passe : " #: templates/registration/password_reset_email.html:9 msgid "Thanks for using our site!" @@ -512,6 +515,7 @@ msgid "Internet access" msgstr "Accès Internet" #: templates/sidebar.html:51 templates/sidebar.html:85 +#, python-format msgid "" "Until\n" " %(end_access_date)s" @@ -526,6 +530,7 @@ msgid "Membership" msgstr "Adhésion" #: templates/sidebar.html:63 templates/sidebar.html:96 +#, python-format msgid "" "Until\n" " %(end_adhesion_date)s" @@ -544,6 +549,7 @@ msgid "You are not logged in." msgstr "Vous n'êtes pas connecté." #: templates/sidebar.html:120 +#, python-format msgid "%(nb)s active machine" msgid_plural "" "%(nb)s\n" @@ -553,4 +559,4 @@ msgstr[1] "%(nb)s machines actives" #: templates/sidebar.html:131 msgid "View my machines" -msgstr "Voir mes machines" \ No newline at end of file +msgstr "Voir mes machines" diff --git a/tickets/locale/fr/LC_MESSAGES/django.po b/tickets/locale/fr/LC_MESSAGES/django.po index e6d840d4..3b764476 100644 --- a/tickets/locale/fr/LC_MESSAGES/django.po +++ b/tickets/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2019-11-16 00:35+0100\n" "Last-Translator: Laouen Fernet \n" "Language-Team: \n" @@ -30,7 +30,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: tickets/forms.py:78 +#: tickets/forms.py:84 msgid "comment" msgstr "commentaire" @@ -206,7 +206,7 @@ msgid "All tickets" msgstr "Tous les tickets" #: tickets/templates/tickets/aff_tickets.html:35 -#: tickets/templates/tickets/edit.html:31 +#: tickets/templates/tickets/edit.html:30 msgid "Ticket" msgid_plural "Tickets" msgstr[0] "Ticket" @@ -267,7 +267,7 @@ msgstr "" msgid "Confirm" msgstr "Confirmer" -#: tickets/templates/tickets/edit.html:34 +#: tickets/templates/tickets/edit.html:33 msgid "Ticket opening" msgstr "Ouverture de ticket" diff --git a/topologie/locale/fr/LC_MESSAGES/django.po b/topologie/locale/fr/LC_MESSAGES/django.po index e99b290d..0dfbdb95 100644 --- a/topologie/locale/fr/LC_MESSAGES/django.po +++ b/topologie/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-25 14:53+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -34,11 +34,11 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: topologie/forms.py:192 +#: topologie/forms.py:216 msgid "Start:" msgstr "Début :" -#: topologie/forms.py:193 +#: topologie/forms.py:217 msgid "End:" msgstr "Fin :" @@ -66,7 +66,7 @@ msgstr "Détails sur l'emplacement du point d'accès sans fil." msgid "Can view an access point object" msgstr "Peut voir un objet point d'accès sans fil" -#: topologie/models.py:110 topologie/views.py:696 topologie/views.py:747 +#: topologie/models.py:110 topologie/views.py:689 topologie/views.py:738 msgid "access point" msgstr "point d'accès sans fil" @@ -98,7 +98,7 @@ msgstr "Provision automatique pour le commutateur réseau." msgid "Can view a switch object" msgstr "Peut voir un objet commutateur réseau" -#: topologie/models.py:286 topologie/views.py:570 topologie/views.py:645 +#: topologie/models.py:286 topologie/views.py:567 topologie/views.py:640 msgid "switch" msgstr "commutateur réseau" @@ -134,7 +134,7 @@ msgstr "Le commutateur réseau est considéré comme un module." msgid "Can view a switch model object" msgstr "Peut voir un objet modèle de commutateur réseau" -#: topologie/models.py:594 topologie/views.py:862 +#: topologie/models.py:594 topologie/views.py:853 msgid "switch model" msgstr "modèle de commutateur réseau" @@ -199,7 +199,7 @@ msgstr "Sur l'emplacement %(slot)s de %(switch)s" msgid "Can view a switch constructor object" msgstr "Peut voir un objet constructeur de commutateur réseau" -#: topologie/models.py:679 topologie/views.py:1102 +#: topologie/models.py:679 topologie/views.py:1093 msgid "switch constructor" msgstr "constructeur de commutateur réseau" @@ -211,7 +211,7 @@ msgstr "constructeurs de commutateur réseau" msgid "Can view a switch bay object" msgstr "Peut voir un objet baie de brassage" -#: topologie/models.py:701 topologie/views.py:922 +#: topologie/models.py:701 topologie/views.py:913 msgid "switch bay" msgstr "baie de brassage" @@ -223,7 +223,7 @@ msgstr "baies de brassage" msgid "Can view a dormitory object" msgstr "Peut voir un objet résidence" -#: topologie/models.py:719 topologie/views.py:1040 +#: topologie/models.py:719 topologie/views.py:1031 msgid "dormitory" msgstr "résidence" @@ -231,67 +231,67 @@ msgstr "résidence" msgid "dormitories" msgstr "résidences" -#: topologie/models.py:751 +#: topologie/models.py:767 msgid "Can view a building object" msgstr "Peut voir un objet bâtiment" -#: topologie/models.py:752 topologie/views.py:980 +#: topologie/models.py:768 topologie/views.py:971 msgid "building" msgstr "bâtiment" -#: topologie/models.py:753 +#: topologie/models.py:769 msgid "buildings" msgstr "bâtiments" -#: topologie/models.py:815 +#: topologie/models.py:847 msgid "Port state Active." msgstr "État du port Actif." -#: topologie/models.py:816 +#: topologie/models.py:848 msgid "port state Active" msgstr "état du port Actif" -#: topologie/models.py:822 +#: topologie/models.py:854 msgid "Can view a port object" msgstr "Peut voir un objet port" -#: topologie/models.py:823 +#: topologie/models.py:855 msgid "port" msgstr "port" -#: topologie/models.py:824 +#: topologie/models.py:856 msgid "ports" msgstr "ports" -#: topologie/models.py:830 +#: topologie/models.py:862 msgid "Uplink: " msgstr "Liaison montante : " -#: topologie/models.py:832 +#: topologie/models.py:864 msgid "Machine: " msgstr "Machine : " -#: topologie/models.py:834 +#: topologie/models.py:866 msgid "Room: " msgstr "Chambre : " -#: topologie/models.py:836 +#: topologie/models.py:868 msgid "Unknown" msgstr "Inconnu" -#: topologie/models.py:895 +#: topologie/models.py:927 msgid "The port can't exist, its number is too great." msgstr "Le port ne peut pas exister, son numéro est trop grand." -#: topologie/models.py:906 +#: topologie/models.py:938 msgid "Room, interface and related port are mutually exclusive." msgstr "Chambre, interface et port relié sont mutuellement exclusifs." -#: topologie/models.py:909 +#: topologie/models.py:941 msgid "A port can't be related to itself." msgstr "Un port ne peut être relié à lui-même." -#: topologie/models.py:914 +#: topologie/models.py:946 msgid "" "The related port is already used, please clear it before creating the " "relation." @@ -299,152 +299,152 @@ msgstr "" "Le port relié est déjà utilisé, veuillez le modifier avant de créer la " "relation." -#: topologie/models.py:942 +#: topologie/models.py:974 msgid "Can view a room object" msgstr "Peut voir un objet chambre" -#: topologie/models.py:943 topologie/views.py:804 +#: topologie/models.py:975 topologie/views.py:795 msgid "room" msgstr "chambre" -#: topologie/models.py:944 +#: topologie/models.py:976 msgid "rooms" msgstr "chambres" -#: topologie/models.py:975 +#: topologie/models.py:1023 msgid "MAC-RADIUS" msgstr "MAC-RADIUS" -#: topologie/models.py:988 topologie/templates/topologie/aff_chambres.html:36 +#: topologie/models.py:1036 topologie/templates/topologie/aff_chambres.html:36 #: topologie/templates/topologie/aff_port.html:38 msgid "Room" msgstr "Chambre" -#: topologie/models.py:989 topologie/templates/topologie/aff_ap.html:36 +#: topologie/models.py:1037 topologie/templates/topologie/aff_ap.html:36 msgid "Access point" msgstr "Point d'accès sans fil" -#: topologie/models.py:990 +#: topologie/models.py:1038 msgid "Uplink" msgstr "Liaison montante" -#: topologie/models.py:991 +#: topologie/models.py:1039 msgid "Organisation machine" msgstr "Machine d'association" -#: topologie/models.py:992 +#: topologie/models.py:1040 msgid "Nothing" msgstr "Rien" -#: topologie/models.py:994 +#: topologie/models.py:1042 msgid "name" msgstr "nom" -#: topologie/models.py:1000 +#: topologie/models.py:1048 msgid "default profile" msgstr "profil par défaut" -#: topologie/models.py:1008 +#: topologie/models.py:1056 msgid "profile on dormitory" msgstr "profil sur la résidence" -#: topologie/models.py:1016 +#: topologie/models.py:1064 msgid "VLAN untagged" msgstr "VLAN untagged" -#: topologie/models.py:1022 +#: topologie/models.py:1070 msgid "VLAN(s) tagged" msgstr "VLAN(s) tagged" -#: topologie/models.py:1027 +#: topologie/models.py:1075 msgid "Type of RADIUS authentication: inactive, MAC-address or 802.1X." msgstr "Type d'authentification RADIUS : inactive, MAC-address ou 802.1X." -#: topologie/models.py:1028 +#: topologie/models.py:1076 msgid "RADIUS type" msgstr "Type de RADIUS" -#: topologie/models.py:1035 +#: topologie/models.py:1083 msgid "In case of MAC-authentication: mode COMMON or STRICT on this port." msgstr "" "Dans le cas d'authentification par adresse MAC : mode COMMON ou STRICT sur " "ce port." -#: topologie/models.py:1037 +#: topologie/models.py:1085 msgid "RADIUS mode" msgstr "Mode de RADIUS" -#: topologie/models.py:1040 +#: topologie/models.py:1088 msgid "Port speed limit." msgstr "Limite de vitesse du port." -#: topologie/models.py:1045 +#: topologie/models.py:1093 msgid "Limit of MAC-address on this port." msgstr "Limite de MAC-address sur ce port." -#: topologie/models.py:1046 +#: topologie/models.py:1094 msgid "MAC limit" msgstr "Limite MAC" -#: topologie/models.py:1048 +#: topologie/models.py:1096 msgid "Flow control." msgstr "Contrôle du flux." -#: topologie/models.py:1051 +#: topologie/models.py:1099 msgid "Protect against rogue DHCP." msgstr "Protège contre les DHCP pirates." -#: topologie/models.py:1052 +#: topologie/models.py:1100 #: topologie/templates/topologie/aff_vlanoptions.html:36 msgid "DHCP snooping" msgstr "DHCP snooping" -#: topologie/models.py:1056 +#: topologie/models.py:1104 msgid "Protect against rogue DHCPv6." msgstr "Protège contre les DHCPv6 pirates." -#: topologie/models.py:1057 +#: topologie/models.py:1105 #: topologie/templates/topologie/aff_vlanoptions.html:37 msgid "DHCPv6 snooping" msgstr "DHCPv6 snooping" -#: topologie/models.py:1061 +#: topologie/models.py:1109 msgid "Check if IP address is DHCP assigned." msgstr "Vérifie si l'adresse IP est attribuée par DHCP." -#: topologie/models.py:1062 +#: topologie/models.py:1110 msgid "ARP protection" msgstr "Protection ARP" -#: topologie/models.py:1066 +#: topologie/models.py:1114 msgid "Protect against rogue RA." msgstr "Protège contre les RA pirates." -#: topologie/models.py:1067 +#: topologie/models.py:1115 msgid "RA guard" msgstr "RA guard" -#: topologie/models.py:1071 +#: topologie/models.py:1119 msgid "Protect against loop." msgstr "Protège contre une boucle." -#: topologie/models.py:1072 +#: topologie/models.py:1120 msgid "loop protection" msgstr "protection contre une boucle" -#: topologie/models.py:1076 +#: topologie/models.py:1124 msgid "Can view a port profile object" msgstr "Peut voir un objet profil de port" -#: topologie/models.py:1077 topologie/views.py:1153 +#: topologie/models.py:1125 topologie/views.py:1144 msgid "port profile" msgstr "profil de port" -#: topologie/models.py:1078 +#: topologie/models.py:1126 msgid "port profiles" msgstr "profils de port" -#: topologie/models.py:1116 +#: topologie/models.py:1164 msgid "A default profile for all dormitories of that type already exists." msgstr "" "Un profil par défaut pour toutes les résidences de ce type existe déjà. " @@ -707,9 +707,9 @@ msgstr "MLD" #: topologie/templates/topologie/index_room.html:30 #: topologie/templates/topologie/index_stack.html:30 #: topologie/templates/topologie/index_switch_bay.html:30 -#: topologie/templates/topologie/switch.html:30 -#: topologie/templates/topologie/topo.html:30 -#: topologie/templates/topologie/topo_more.html:30 +#: topologie/templates/topologie/switch.html:29 +#: topologie/templates/topologie/topo.html:29 +#: topologie/templates/topologie/topo_more.html:29 msgid "Topology" msgstr "Topologie" @@ -723,8 +723,8 @@ msgstr "" "( %(objet)s ) ?" #: topologie/templates/topologie/delete.html:36 -#: topologie/templates/topologie/switch.html:46 -#: topologie/templates/topologie/topo_more.html:59 +#: topologie/templates/topologie/switch.html:45 +#: topologie/templates/topologie/topo_more.html:60 msgid "Confirm" msgstr "Confirmer" @@ -828,91 +828,91 @@ msgstr "Baies de brassage" msgid "Add a switch bay" msgstr "Ajouter une baie de brassage" -#: topologie/templates/topologie/switch.html:39 -#: topologie/templates/topologie/topo.html:36 +#: topologie/templates/topologie/switch.html:38 +#: topologie/templates/topologie/topo.html:35 msgid "Go to the ports list" msgstr "Aller à la liste des ports" -#: topologie/templates/topologie/switch.html:43 +#: topologie/templates/topologie/switch.html:42 msgid "Specific settings for the switch" msgstr "Réglages spécifiques pour le commutateur réseau" -#: topologie/templates/topologie/topo_more.html:48 +#: topologie/templates/topologie/topo_more.html:49 #, python-format msgid "Specific settings for the %(device)s object" msgstr "Réglages spécifiques pour l'objet %(device)s" -#: topologie/templates/topologie/topo_more.html:52 +#: topologie/templates/topologie/topo_more.html:53 #, python-format msgid "General settings for the machine linked to the %(device)s object" msgstr "Réglages généraux pour la machine liée à l'objet %(device)s" -#: topologie/templates/topologie/topo_more.html:56 +#: topologie/templates/topologie/topo_more.html:57 msgid "DNS name" msgstr "Nom DNS" -#: topologie/views.py:375 +#: topologie/views.py:374 msgid "The VLAN was edited." msgstr "Le VLAN a été modifié." -#: topologie/views.py:378 topologie/views.py:429 topologie/views.py:486 -#: topologie/views.py:779 topologie/views.py:835 topologie/views.py:895 -#: topologie/views.py:955 topologie/views.py:1013 topologie/views.py:1075 -#: topologie/views.py:1135 topologie/views.py:1184 topologie/views.py:1240 +#: topologie/views.py:377 topologie/views.py:428 topologie/views.py:485 +#: topologie/views.py:770 topologie/views.py:826 topologie/views.py:886 +#: topologie/views.py:946 topologie/views.py:1004 topologie/views.py:1066 +#: topologie/views.py:1126 topologie/views.py:1175 topologie/views.py:1231 msgid "Edit" msgstr "Modifier" -#: topologie/views.py:389 topologie/views.py:584 +#: topologie/views.py:388 topologie/views.py:581 msgid "Nonexistent switch." msgstr "Commutateur réseau inexistant." -#: topologie/views.py:397 +#: topologie/views.py:396 msgid "The port was added." msgstr "Le port a été ajouté." -#: topologie/views.py:399 +#: topologie/views.py:398 msgid "The port already exists." msgstr "Le port existe déjà." -#: topologie/views.py:402 topologie/views.py:471 topologie/views.py:764 -#: topologie/views.py:818 topologie/views.py:878 topologie/views.py:938 -#: topologie/views.py:996 topologie/views.py:1056 topologie/views.py:1118 -#: topologie/views.py:1169 topologie/views.py:1223 +#: topologie/views.py:401 topologie/views.py:470 topologie/views.py:755 +#: topologie/views.py:809 topologie/views.py:869 topologie/views.py:929 +#: topologie/views.py:987 topologie/views.py:1047 topologie/views.py:1109 +#: topologie/views.py:1160 topologie/views.py:1214 msgid "Add" msgstr "Ajouter" -#: topologie/views.py:419 +#: topologie/views.py:418 msgid "The port was edited." msgstr "Le port a été modifié." -#: topologie/views.py:443 +#: topologie/views.py:442 msgid "The port was deleted." msgstr "Le port a été supprimé." -#: topologie/views.py:449 +#: topologie/views.py:448 #, python-format msgid "The port %s is used by another object, impossible to delete it." msgstr "Le port %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:468 +#: topologie/views.py:467 msgid "The stack was created." msgstr "La pile a été créée." -#: topologie/views.py:483 +#: topologie/views.py:482 msgid "The stack was edited." msgstr "La pile a été modifiée." -#: topologie/views.py:497 +#: topologie/views.py:496 msgid "The stack was deleted." msgstr "La pile a été supprimée." -#: topologie/views.py:503 +#: topologie/views.py:502 #, python-format msgid "The stack %s is used by another object, impossible to deleted it." msgstr "" "La pile %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:544 topologie/views.py:670 topologie/views.py:723 +#: topologie/views.py:543 topologie/views.py:665 topologie/views.py:716 msgid "" "The organisation's user doesn't exist yet, please create or link it in the " "preferences." @@ -920,131 +920,131 @@ msgstr "" "L'utilisateur de l'association n'existe pas encore, veuillez le créer ou le " "relier dans les préférences." -#: topologie/views.py:561 +#: topologie/views.py:560 msgid "The switch was created." msgstr "Le commutateur réseau a été créé." -#: topologie/views.py:597 +#: topologie/views.py:594 msgid "The ports were created." msgstr "Les ports ont été créés." -#: topologie/views.py:635 +#: topologie/views.py:632 msgid "The switch was edited." msgstr "Le commutateur réseau a été modifié." -#: topologie/views.py:687 +#: topologie/views.py:682 msgid "The access point was created." msgstr "Le point d'accès sans fil a été créé." -#: topologie/views.py:738 +#: topologie/views.py:731 msgid "The access point was edited." msgstr "Le point d'accès sans fil a été modifié." -#: topologie/views.py:761 +#: topologie/views.py:752 msgid "The room was created." msgstr "La chambre a été créée." -#: topologie/views.py:776 +#: topologie/views.py:767 msgid "The room was edited." msgstr "La chambre a été modifiée." -#: topologie/views.py:790 +#: topologie/views.py:781 msgid "The room was deleted." msgstr "La chambre a été supprimée." -#: topologie/views.py:796 +#: topologie/views.py:787 #, python-format msgid "The room %s is used by another object, impossible to deleted it." msgstr "" "La chambre %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:815 +#: topologie/views.py:806 msgid "The switch model was created." msgstr "Le modèle de commutateur réseau a été créé." -#: topologie/views.py:832 +#: topologie/views.py:823 msgid "The switch model was edited." msgstr "Le modèle de commutateur réseau a été modifié." -#: topologie/views.py:848 +#: topologie/views.py:839 msgid "The switch model was deleted." msgstr "Le modèle de commutateur réseau a été supprimé." -#: topologie/views.py:854 +#: topologie/views.py:845 #, python-format msgid "The switch model %s is used by another object, impossible to delete it." msgstr "" "Le modèle de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: topologie/views.py:875 +#: topologie/views.py:866 msgid "The switch bay was created." msgstr "La baie de brassage a été créée." -#: topologie/views.py:892 +#: topologie/views.py:883 msgid "The switch bay was edited." msgstr "La baie de brassage a été modifiée." -#: topologie/views.py:908 +#: topologie/views.py:899 msgid "The switch bay was deleted." msgstr "La baie de brassage a été supprimée." -#: topologie/views.py:914 +#: topologie/views.py:905 #, python-format msgid "The switch bay %s is used by another object, impossible to delete it." msgstr "" "La baie de brassage %s est utilisée par un autre objet, impossible de la " "supprimer." -#: topologie/views.py:935 +#: topologie/views.py:926 msgid "The building was created." msgstr "Le bâtiment a été créé." -#: topologie/views.py:952 +#: topologie/views.py:943 msgid "The building was edited." msgstr "Le bâtiment a été modifié." -#: topologie/views.py:966 +#: topologie/views.py:957 msgid "The building was deleted." msgstr "Le bâtiment a été supprimé." -#: topologie/views.py:972 +#: topologie/views.py:963 #, python-format msgid "The building %s is used by another object, impossible to delete it." msgstr "" "Le bâtiment %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:993 +#: topologie/views.py:984 msgid "The dormitory was created." msgstr "La résidence a été créée." -#: topologie/views.py:1010 +#: topologie/views.py:1001 msgid "The dormitory was edited." msgstr "La résidence a été modifiée." -#: topologie/views.py:1026 +#: topologie/views.py:1017 msgid "The dormitory was deleted." msgstr "La résidence a été supprimée." -#: topologie/views.py:1032 +#: topologie/views.py:1023 #, python-format msgid "The dormitory %s is used by another object, impossible to delete it." msgstr "" "La résidence %s est utilisée par un autre objet, impossible de la supprimer." -#: topologie/views.py:1053 +#: topologie/views.py:1044 msgid "The switch constructor was created." msgstr "Le constructeur de commutateur réseau a été créé." -#: topologie/views.py:1072 +#: topologie/views.py:1063 msgid "The switch constructor was edited." msgstr "Le constructeur de commutateur réseau a été modifié." -#: topologie/views.py:1088 +#: topologie/views.py:1079 msgid "The switch constructor was deleted." msgstr "Le constructeur de commutateur réseau a été supprimé." -#: topologie/views.py:1094 +#: topologie/views.py:1085 #, python-format msgid "" "The switch constructor %s is used by another object, impossible to delete it." @@ -1052,49 +1052,49 @@ msgstr "" "Le constructeur de commutateur réseau %s est utilisé par un autre objet, " "impossible de le supprimer." -#: topologie/views.py:1115 +#: topologie/views.py:1106 msgid "The port profile was created." msgstr "Le profil de port a été créé." -#: topologie/views.py:1132 +#: topologie/views.py:1123 msgid "The port profile was edited." msgstr "Le profil de port a été modifié." -#: topologie/views.py:1148 +#: topologie/views.py:1139 msgid "The port profile was deleted." msgstr "Le profil de port a été supprimé." -#: topologie/views.py:1150 +#: topologie/views.py:1141 msgid "Impossible to delete the port profile." msgstr "Impossible de supprimer le profil de port." -#: topologie/views.py:1166 +#: topologie/views.py:1157 msgid "The module was created." msgstr "Le module a été créé." -#: topologie/views.py:1181 topologie/views.py:1237 +#: topologie/views.py:1172 topologie/views.py:1228 msgid "The module was edited." msgstr "Le module a été modifié." -#: topologie/views.py:1195 topologie/views.py:1251 +#: topologie/views.py:1186 topologie/views.py:1242 msgid "The module was deleted." msgstr "Le module a été supprimé." -#: topologie/views.py:1201 topologie/views.py:1257 +#: topologie/views.py:1192 topologie/views.py:1248 #, python-format msgid "The module %s is used by another object, impossible to deleted it." msgstr "" "Le module %s est utilisé par un autre objet, impossible de le supprimer." -#: topologie/views.py:1209 topologie/views.py:1265 +#: topologie/views.py:1200 topologie/views.py:1256 msgid "module" msgstr "module" -#: topologie/views.py:1220 +#: topologie/views.py:1211 msgid "The module was added." msgstr "Le module a été ajouté." -#: topologie/views.py:1434 +#: topologie/views.py:1425 msgid "" "The default Django template isn't used. This can lead to rendering errors. " "Check the parameters." diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 0c843b6c..20fba98d 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -21,7 +21,7 @@ msgid "" msgstr "" "Project-Id-Version: 2.5\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-11-17 22:00+0100\n" +"POT-Creation-Date: 2021-01-24 11:51+0100\n" "PO-Revision-Date: 2018-06-27 23:35+0200\n" "Last-Translator: Yoann Piétri \n" "Language-Team: \n" @@ -35,39 +35,40 @@ msgstr "" msgid "You don't have the right to view this application." msgstr "Vous n'avez pas le droit de voir cette application." -#: users/forms.py:96 users/forms.py:159 users/forms.py:438 users/models.py:2728 +#: users/forms.py:103 users/forms.py:166 users/forms.py:447 +#: users/models.py:2582 msgid "Password" msgstr "Mot de passe" -#: users/forms.py:103 users/forms.py:164 users/forms.py:445 +#: users/forms.py:110 users/forms.py:169 users/forms.py:454 msgid "Password confirmation" msgstr "Confirmation du mot de passe" -#: users/forms.py:130 users/forms.py:189 users/forms.py:515 +#: users/forms.py:137 users/forms.py:192 users/forms.py:524 msgid "The passwords don't match." msgstr "Les mots de passe ne correspondent pas." -#: users/forms.py:219 +#: users/forms.py:223 msgid "Current password" msgstr "Mot de passe actuel" -#: users/forms.py:222 users/forms.py:671 users/forms.py:710 +#: users/forms.py:226 users/forms.py:697 users/forms.py:736 msgid "New password" msgstr "Nouveau mot de passe" -#: users/forms.py:228 +#: users/forms.py:232 msgid "New password confirmation" msgstr "Confirmation du nouveau mot de passe" -#: users/forms.py:250 +#: users/forms.py:252 msgid "The new passwords don't match." msgstr "Les nouveaux mots de passe ne correspondent pas." -#: users/forms.py:262 +#: users/forms.py:264 msgid "The current password is incorrect." msgstr "Le mot de passe actuel est incorrect." -#: users/forms.py:285 users/templates/users/aff_clubs.html:38 +#: users/forms.py:287 users/templates/users/aff_clubs.html:38 #: users/templates/users/aff_listright.html:63 #: users/templates/users/aff_listright.html:168 #: users/templates/users/aff_users.html:39 @@ -76,7 +77,7 @@ msgstr "Le mot de passe actuel est incorrect." msgid "Username" msgstr "Pseudo" -#: users/forms.py:300 +#: users/forms.py:301 msgid "Fully archive users? WARNING: CRITICAL OPERATION IF TRUE" msgstr "" "Complètement archiver les utilisateurs ? ATTENTION: OPÉRATION CRITIQUE SI OUI" @@ -99,48 +100,48 @@ msgstr "Prénom" msgid "Surname" msgstr "Nom" -#: users/forms.py:333 users/forms.py:591 users/models.py:2728 +#: users/forms.py:333 users/forms.py:604 users/models.py:2582 #: users/templates/users/aff_emailaddress.html:36 #: users/templates/users/profil.html:220 msgid "Email address" msgstr "Adresse mail" -#: users/forms.py:334 users/forms.py:589 users/forms.py:776 +#: users/forms.py:334 users/forms.py:602 users/forms.py:802 #: users/templates/users/aff_schools.html:37 #: users/templates/users/profil.html:241 msgid "School" msgstr "Établissement" -#: users/forms.py:335 users/forms.py:590 +#: users/forms.py:335 users/forms.py:603 #: users/templates/users/aff_serviceusers.html:34 #: users/templates/users/profil.html:246 msgid "Comment" msgstr "Commentaire" -#: users/forms.py:337 users/forms.py:593 +#: users/forms.py:337 users/forms.py:606 #: users/templates/users/aff_clubs.html:40 #: users/templates/users/aff_users.html:41 #: users/templates/users/profil.html:225 msgid "Room" msgstr "Chambre" -#: users/forms.py:338 users/forms.py:594 +#: users/forms.py:338 users/forms.py:607 msgid "No room" msgstr "Pas de chambre" -#: users/forms.py:339 users/forms.py:595 +#: users/forms.py:339 users/forms.py:608 msgid "Select a school" msgstr "Sélectionnez un établissement" -#: users/forms.py:355 +#: users/forms.py:365 msgid "Force the move?" msgstr "Forcer le déménagement ?" -#: users/forms.py:370 users/forms.py:625 +#: users/forms.py:380 users/forms.py:643 msgid "A valid telephone number is required." msgstr "Un numéro de téléphone valide est requis." -#: users/forms.py:418 +#: users/forms.py:429 msgid "" "If this options is set, you will receive a link to set your initial password " "by email. If you do not have any means of accessing your emails, you can " @@ -155,11 +156,11 @@ msgstr "" "votre adresse dans les délais impartis, votre connexion sera automatiquement " "suspendue." -#: users/forms.py:432 +#: users/forms.py:441 msgid "Send password reset link by email." msgstr "Envoyer le lien de modification du mot de passe par mail." -#: users/forms.py:452 +#: users/forms.py:461 msgid "" "If you already have an account, please use it. If your lost access to it, " "please consider using the forgotten password button on the login page or " @@ -170,271 +171,271 @@ msgstr "" "passe oublié est à votre disposition. Si vous avez oublié votre login, " "contactez le support." -#: users/forms.py:459 +#: users/forms.py:468 msgid "I certify that I have not had an account before." msgstr "Je certifie sur l'honneur ne pas déjà avoir de compte." -#: users/forms.py:485 +#: users/forms.py:494 msgid "I commit to accept the" msgstr "J'accepte les" -#: users/forms.py:487 +#: users/forms.py:496 msgid "General Terms of Use" msgstr "Conditions Générales d'Utilisation" -#: users/forms.py:552 +#: users/forms.py:565 msgid "Leave empty if you don't have any GPG key." msgstr "Laissez vide si vous n'avez pas de clé GPG." -#: users/forms.py:557 +#: users/forms.py:570 msgid "Default shell" msgstr "Interface en ligne de commande par défaut" -#: users/forms.py:588 users/templates/users/aff_clubs.html:36 +#: users/forms.py:601 users/templates/users/aff_clubs.html:36 #: users/templates/users/aff_serviceusers.html:32 msgid "Name" msgstr "Nom" -#: users/forms.py:596 +#: users/forms.py:609 msgid "Use a mailing list" msgstr "Utiliser une liste de diffusion" -#: users/forms.py:736 users/templates/users/profil.html:288 +#: users/forms.py:762 users/templates/users/profil.html:288 msgid "State" msgstr "État" -#: users/forms.py:737 +#: users/forms.py:763 msgid "Email state" msgstr "État du mail" -#: users/forms.py:759 users/templates/users/aff_listright.html:38 +#: users/forms.py:785 users/templates/users/aff_listright.html:38 msgid "Superuser" msgstr "Superutilisateur" -#: users/forms.py:793 +#: users/forms.py:819 msgid "Shell name" msgstr "Nom de l'interface en ligne de commande" -#: users/forms.py:818 +#: users/forms.py:844 msgid "Name of the group of rights" msgstr "Nom du groupe de droits" -#: users/forms.py:835 +#: users/forms.py:861 msgid "GID. Warning: this field must not be edited after creation." msgstr "GID. Attention : ce champ ne doit pas être modifié après création." -#: users/forms.py:849 +#: users/forms.py:875 msgid "Current groups of rights" msgstr "Groupes de droits actuels" -#: users/forms.py:872 +#: users/forms.py:898 msgid "Current schools" msgstr "Établissements actuels" -#: users/forms.py:895 users/forms.py:914 users/templates/users/aff_bans.html:41 -#: users/templates/users/aff_whitelists.html:41 +#: users/forms.py:921 users/forms.py:940 users/templates/users/aff_bans.html:41 +#: users/templates/users/aff_whitelists.html:42 msgid "End date" msgstr "Date de fin" -#: users/forms.py:934 +#: users/forms.py:960 msgid "Local part of the email address" msgstr "Partie locale de l'adresse mail" -#: users/forms.py:935 +#: users/forms.py:961 msgid "Can't contain @." msgstr "Ne peut pas contenir @." -#: users/forms.py:956 +#: users/forms.py:982 msgid "Main email address" msgstr "Adresse mail principale" -#: users/forms.py:959 +#: users/forms.py:985 msgid "Redirect local emails" msgstr "Rediriger les mails locaux" -#: users/forms.py:961 +#: users/forms.py:987 msgid "Use local emails" msgstr "Utiliser les mails locaux" -#: users/forms.py:1007 +#: users/forms.py:1034 msgid "This room is my room" msgstr "Il s'agit bien de ma chambre" -#: users/forms.py:1012 +#: users/forms.py:1039 msgid "This new connected device is mine" msgstr "Ce nouvel appareil connecté m'appartient" -#: users/models.py:130 +#: users/models.py:122 #, python-format msgid "The username \"%(label)s\" contains forbidden characters." msgstr "Le pseudo « %(label)s » contient des caractères interdits." -#: users/models.py:167 +#: users/models.py:159 msgid "Users must have an username." msgstr "Les utilisateurs doivent avoir un pseudo." -#: users/models.py:170 +#: users/models.py:162 msgid "Username should only contain [a-z0-9-]." msgstr "Le pseudo devrait seulement contenir [a-z0-9-]" -#: users/models.py:232 users/templates/users/aff_clubs.html:55 +#: users/models.py:224 users/templates/users/aff_clubs.html:55 #: users/templates/users/aff_users.html:57 #: users/templates/users/profil.html:290 msgid "Active" msgstr "Actif" -#: users/models.py:233 users/templates/users/aff_clubs.html:57 +#: users/models.py:225 users/templates/users/aff_clubs.html:57 #: users/templates/users/aff_users.html:59 #: users/templates/users/profil.html:292 users/templates/users/profil.html:308 msgid "Disabled" msgstr "Désactivé" -#: users/models.py:234 users/templates/users/profil.html:294 +#: users/models.py:226 users/templates/users/profil.html:294 msgid "Archived" msgstr "Archivé" -#: users/models.py:235 users/templates/users/profil.html:296 +#: users/models.py:227 users/templates/users/profil.html:296 msgid "Not yet active" msgstr "Pas encore adhéré" -#: users/models.py:236 users/templates/users/profil.html:298 +#: users/models.py:228 users/templates/users/profil.html:298 msgid "Fully archived" msgstr "Complètement archivé" -#: users/models.py:243 +#: users/models.py:235 msgid "Confirmed" msgstr "Confirmé" -#: users/models.py:244 +#: users/models.py:236 msgid "Not confirmed" msgstr "Non confirmé" -#: users/models.py:245 +#: users/models.py:237 msgid "Waiting for email confirmation" msgstr "En attente de confirmation" -#: users/models.py:252 users/models.py:2237 +#: users/models.py:244 users/models.py:2151 msgid "Must only contain letters, numerals or dashes." msgstr "Doit seulement contenir des lettres, chiffres ou tirets." -#: users/models.py:258 +#: users/models.py:250 msgid "External email address allowing us to contact you." msgstr "Adresse mail externe nous permettant de vous contacter." -#: users/models.py:263 +#: users/models.py:255 msgid "" "Enable redirection of the local email messages to the main email address." msgstr "" "Activer la redirection des mails locaux vers l'adresse mail principale." -#: users/models.py:269 +#: users/models.py:261 msgid "Enable the local email account." msgstr "Activer le compte mail local" -#: users/models.py:276 +#: users/models.py:268 msgid "Education institute." msgstr "Etablissement d'enseignement" -#: users/models.py:283 +#: users/models.py:275 msgid "Unix shell." msgstr "Interface en ligne de commande" -#: users/models.py:286 +#: users/models.py:278 msgid "Comment, school year." msgstr "Commentaire, promotion." -#: users/models.py:292 +#: users/models.py:284 msgid "Account state." msgstr "Etat du compte" -#: users/models.py:302 +#: users/models.py:294 msgid "Optionnal legacy uid, for import and transition purpose" msgstr "Uid legacy optionnel, pour aider les imports et transitions" -#: users/models.py:305 +#: users/models.py:297 msgid "enable shortcuts on Re2o website" msgstr "activer les raccourcis sur le site de Re2o" -#: users/models.py:318 +#: users/models.py:310 msgid "Can change the password of a user" msgstr "Peut changer le mot de passe d'un utilisateur" -#: users/models.py:319 +#: users/models.py:311 msgid "Can edit the state of a user" msgstr "Peut changer l'état d'un utilisateur" -#: users/models.py:320 +#: users/models.py:312 msgid "Can force the move" msgstr "Peut forcer le déménagement" -#: users/models.py:321 +#: users/models.py:313 msgid "Can edit the shell of a user" msgstr "Peut modifier l'interface en ligne de commande d'un utilisateur" -#: users/models.py:322 +#: users/models.py:314 msgid "Can edit the pseudo of a user" msgstr "Peut changer le pseudo d'un utilisateur" -#: users/models.py:325 +#: users/models.py:317 msgid "Can edit the groups of rights of a user (critical permission)" msgstr "" "Peut modifier les groupes de droits d'un utilisateur (permission critique)" -#: users/models.py:327 +#: users/models.py:319 msgid "Can edit all users, including those with rights" msgstr "" "Peut modifier tous les utilisateurs, y compris ceux possédant des droits" -#: users/models.py:328 +#: users/models.py:320 msgid "Can view a user object" msgstr "Peut voir un objet utilisateur" -#: users/models.py:330 +#: users/models.py:322 msgid "user (member or club)" msgstr "utilisateur (adhérent ou club)" -#: users/models.py:331 +#: users/models.py:323 msgid "users (members or clubs)" msgstr "utilisateurs (adhérents ou clubs)" -#: users/models.py:367 users/models.py:420 users/models.py:438 +#: users/models.py:359 users/models.py:412 users/models.py:430 msgid "Unknown type." msgstr "Type inconnu." -#: users/models.py:434 users/templates/users/aff_listright.html:75 +#: users/models.py:426 users/templates/users/aff_listright.html:75 #: users/templates/users/aff_listright.html:180 msgid "Member" msgstr "Adhérent" -#: users/models.py:436 +#: users/models.py:428 msgid "Club" msgstr "Club" -#: users/models.py:1452 +#: users/models.py:1341 msgid "Maximum number of registered machines reached." msgstr "Nombre maximum de machines enregistrées atteint." -#: users/models.py:1454 +#: users/models.py:1343 msgid "Re2o doesn't know wich machine type to assign." msgstr "Re2o ne sait pas quel type de machine attribuer." -#: users/models.py:1477 users/templates/users/user_autocapture.html:64 +#: users/models.py:1366 users/templates/users/user_autocapture.html:63 msgid "OK" msgstr "OK" -#: users/models.py:1572 +#: users/models.py:1461 msgid "This user is archived." msgstr "Cet utilisateur est archivé." -#: users/models.py:1586 users/models.py:1640 +#: users/models.py:1475 users/models.py:1529 msgid "You don't have the right to edit this club." msgstr "Vous n'avez pas le droit de modifier ce club." -#: users/models.py:1598 +#: users/models.py:1487 msgid "User with critical rights, can't be edited." msgstr "Utilisateur avec des droits critiques, ne peut être modifié." -#: users/models.py:1605 +#: users/models.py:1494 msgid "" "Impossible to edit the organisation's user without the \"change_all_users\" " "right." @@ -442,261 +443,267 @@ msgstr "" "Impossible de modifier l'utilisateur de l'association sans le droit « " "change_all_users »." -#: users/models.py:1617 users/models.py:1655 +#: users/models.py:1506 users/models.py:1544 msgid "You don't have the right to edit another user." msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." -#: users/models.py:1681 +#: users/models.py:1570 msgid "You don't have the right to change the room." msgstr "Vous n'avez pas le droit de changer la chambre." -#: users/models.py:1698 +#: users/models.py:1587 msgid "You don't have the right to change the state." msgstr "Vous n'avez pas le droit de changer l'état." -#: users/models.py:1718 +#: users/models.py:1607 msgid "You don't have the right to change the shell." msgstr "Vous n'avez pas le droit de changer l'interface en ligne de commande." -#: users/models.py:1741 +#: users/models.py:1630 msgid "You don't have the right to change the pseudo." msgstr "Vous n'avez pas le droit de changer le pseudo." -#: users/models.py:1758 users/models.py:1773 +#: users/models.py:1647 users/models.py:1662 msgid "Local email accounts must be enabled." msgstr "Les comptes mail locaux doivent être activés." -#: users/models.py:1788 +#: users/models.py:1677 msgid "You don't have the right to force the move." msgstr "Vous n'avez pas le droit de forcer le déménagement." -#: users/models.py:1803 +#: users/models.py:1692 msgid "You don't have the right to edit the user's groups of rights." msgstr "" "Vous n'avez pas le droit de modifier les groupes de droits d'un autre " "utilisateur." -#: users/models.py:1820 +#: users/models.py:1709 msgid "\"superuser\" right required to edit the superuser flag." msgstr "Droit « superuser » requis pour modifier le signalement superuser." -#: users/models.py:1846 +#: users/models.py:1735 msgid "You don't have the right to view this club." msgstr "Vous n'avez pas le droit de voir ce club." -#: users/models.py:1855 +#: users/models.py:1744 msgid "You don't have the right to view another user." msgstr "Vous n'avez pas le droit de voir un autre utilisateur." -#: users/models.py:1871 users/models.py:2152 +#: users/models.py:1760 users/models.py:2068 msgid "You don't have the right to view the list of users." msgstr "Vous n'avez pas le droit de voir la liste des utilisateurs." -#: users/models.py:1888 +#: users/models.py:1777 msgid "You don't have the right to delete this user." msgstr "Vous n'avez pas le droit de supprimer cet utilisateur." -#: users/models.py:1920 +#: users/models.py:1809 msgid "This username is already used." msgstr "Ce pseudo est déjà utilisé." -#: users/models.py:1940 +#: users/models.py:1829 msgid "Email field cannot be empty." msgstr "Le champ mail ne peut pas ^êêtre vide" -#: users/models.py:1947 +#: users/models.py:1836 msgid "You can't use a {} address as an external contact address." msgstr "Vous ne pouvez pas utiliser une adresse {} pour votre adresse externe." -#: users/models.py:1993 +#: users/models.py:1882 msgid "member" msgstr "adhérent" -#: users/models.py:1994 +#: users/models.py:1883 msgid "members" msgstr "adhérents" -#: users/models.py:2025 +#: users/models.py:1914 msgid "A GPG fingerprint must contain 40 hexadecimal characters." msgstr "Une empreinte GPG doit contenir 40 caractères hexadécimaux." -#: users/models.py:2052 +#: users/models.py:1941 msgid "Self registration is disabled." msgstr "L'auto inscription est désactivée." -#: users/models.py:2062 +#: users/models.py:1951 msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." -#: users/models.py:2105 +#: users/models.py:1979 +#, fuzzy +#| msgid "You don't have the right to edit another user." +msgid "You don't have the right to list all adherents." +msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." + +#: users/models.py:2021 msgid "club" msgstr "club" -#: users/models.py:2106 +#: users/models.py:2022 msgid "clubs" msgstr "clubs" -#: users/models.py:2117 +#: users/models.py:2033 msgid "You must be authenticated." msgstr "Vous devez être authentifié." -#: users/models.py:2125 +#: users/models.py:2041 msgid "You don't have the right to create a club." msgstr "Vous n'avez pas le droit de créer un club." -#: users/models.py:2241 +#: users/models.py:2155 msgid "Comment." msgstr "Commentaire." -#: users/models.py:2247 +#: users/models.py:2161 msgid "Can view a service user object" msgstr "Peut voir un objet utilisateur service" -#: users/models.py:2248 users/views.py:496 +#: users/models.py:2162 users/views.py:496 msgid "service user" msgstr "utilisateur service" -#: users/models.py:2249 +#: users/models.py:2163 msgid "service users" msgstr "utilisateurs service" -#: users/models.py:2258 +#: users/models.py:2172 #, python-brace-format msgid "Service user <{name}>" msgstr "Utilisateur service <{name}>" -#: users/models.py:2363 +#: users/models.py:2218 msgid "Can view a school object" msgstr "Peut voir un objet établissement" -#: users/models.py:2364 +#: users/models.py:2219 msgid "school" msgstr "établissement" -#: users/models.py:2365 +#: users/models.py:2220 msgid "schools" msgstr "établissements" -#: users/models.py:2392 +#: users/models.py:2263 msgid "UNIX group names can only contain lower case letters." msgstr "" "Les noms de groupe UNIX peuvent seulement contenir des lettres minuscules." -#: users/models.py:2398 +#: users/models.py:2269 msgid "Description." msgstr "Description." -#: users/models.py:2401 +#: users/models.py:2272 msgid "Can view a group of rights object" msgstr "Peut voir un objet groupe de droits" -#: users/models.py:2402 +#: users/models.py:2273 msgid "group of rights" msgstr "groupe de droits" -#: users/models.py:2403 +#: users/models.py:2274 msgid "groups of rights" msgstr "groupes de droits" -#: users/models.py:2474 +#: users/models.py:2312 msgid "Can view a shell object" msgstr "Peut voir un objet interface en ligne de commande" -#: users/models.py:2475 users/views.py:963 +#: users/models.py:2313 users/views.py:963 msgid "shell" msgstr "interface en ligne de commande" -#: users/models.py:2476 +#: users/models.py:2314 msgid "shells" msgstr "interfaces en ligne de commande" -#: users/models.py:2511 +#: users/models.py:2365 msgid "HARD (no access)" msgstr "HARD (pas d'accès)" -#: users/models.py:2512 +#: users/models.py:2366 msgid "SOFT (local access only)" msgstr "SOFT (accès local uniquement)" -#: users/models.py:2513 +#: users/models.py:2367 msgid "RESTRICTED (speed limitation)" msgstr "RESTRICTED (limitation de vitesse)" -#: users/models.py:2524 +#: users/models.py:2378 msgid "Can view a ban object" msgstr "Peut voir un objet bannissement" -#: users/models.py:2525 users/views.py:578 +#: users/models.py:2379 users/views.py:578 msgid "ban" msgstr "bannissement" -#: users/models.py:2526 +#: users/models.py:2380 msgid "bans" msgstr "bannissements" -#: users/models.py:2580 +#: users/models.py:2434 msgid "You don't have the right to view other bans than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres bannissements que les vôtres." -#: users/models.py:2641 +#: users/models.py:2495 msgid "Can view a whitelist object" msgstr "Peut voir un objet accès gracieux" -#: users/models.py:2642 +#: users/models.py:2496 msgid "whitelist (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:2643 +#: users/models.py:2497 msgid "whitelists (free of charge access)" msgstr "Accès gracieux" -#: users/models.py:2671 +#: users/models.py:2525 msgid "You don't have the right to view other whitelists than yours." msgstr "" "Vous n'avez pas le droit de voir d'autres accès gracieux que les vôtres." -#: users/models.py:2756 +#: users/models.py:2610 msgid "User of the local email account." msgstr "Utilisateur du compte mail local." -#: users/models.py:2759 +#: users/models.py:2613 msgid "Local part of the email address." msgstr "Partie locale de l'adresse mail." -#: users/models.py:2764 +#: users/models.py:2618 msgid "Can view a local email account object" msgstr "Peut voir un objet compte mail local" -#: users/models.py:2766 +#: users/models.py:2620 msgid "local email account" msgstr "compte mail local" -#: users/models.py:2767 +#: users/models.py:2621 msgid "local email accounts" msgstr "comptes mail locaux" -#: users/models.py:2805 users/models.py:2840 users/models.py:2874 -#: users/models.py:2908 +#: users/models.py:2659 users/models.py:2694 users/models.py:2728 +#: users/models.py:2762 msgid "The local email accounts are not enabled." msgstr "Les comptes mail locaux ne sont pas activés." -#: users/models.py:2810 +#: users/models.py:2664 msgid "You don't have the right to add a local email account to another user." msgstr "" "Vous n'avez pas le droit d'ajouter un compte mail local à un autre " "utilisateur." -#: users/models.py:2820 +#: users/models.py:2674 msgid "You reached the limit of {} local email accounts." msgstr "Vous avez atteint la limite de {} comptes mail locaux." -#: users/models.py:2846 +#: users/models.py:2700 msgid "You don't have the right to view another user's local email account." msgstr "" "Vous n'avez pas le droit de voir le compte mail local d'un autre utilisateur." -#: users/models.py:2866 +#: users/models.py:2720 msgid "" "You can't delete a local email account whose local part is the same as the " "username." @@ -704,13 +711,13 @@ msgstr "" "Vous ne pouvez pas supprimer un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2880 +#: users/models.py:2734 msgid "You don't have the right to delete another user's local email account." msgstr "" "Vous n'avez pas le droit de supprimer le compte mail local d'un autre " "utilisateur." -#: users/models.py:2900 +#: users/models.py:2754 msgid "" "You can't edit a local email account whose local part is the same as the " "username." @@ -718,28 +725,28 @@ msgstr "" "Vous ne pouvez pas modifier un compte mail local dont la partie locale est " "la même que le pseudo." -#: users/models.py:2914 +#: users/models.py:2768 msgid "You don't have the right to edit another user's local email account." msgstr "" "Vous n'avez pas le droit de modifier le compte mail local d'un autre " "utilisateur." -#: users/models.py:2934 +#: users/models.py:2788 msgid "The local part must not contain @ or +." msgstr "La partie locale ne doit pas contenir @ ou +." #: users/templates/users/aff_bans.html:36 -#: users/templates/users/aff_whitelists.html:36 +#: users/templates/users/aff_whitelists.html:37 msgid "User" msgstr "Utilisateur" #: users/templates/users/aff_bans.html:38 -#: users/templates/users/aff_whitelists.html:38 +#: users/templates/users/aff_whitelists.html:39 msgid "Reason" msgstr "Raison" #: users/templates/users/aff_bans.html:39 -#: users/templates/users/aff_whitelists.html:39 +#: users/templates/users/aff_whitelists.html:40 msgid "Start date" msgstr "Date de début" @@ -844,8 +851,8 @@ msgstr[1] "Total: %(perm_count)s permissions" #: users/templates/users/index_serviceusers.html:30 #: users/templates/users/index_shell.html:30 #: users/templates/users/index_whitelist.html:29 -#: users/templates/users/plugin_out.html:31 users/templates/users/user.html:30 -#: users/templates/users/user_autocapture.html:30 +#: users/templates/users/plugin_out.html:30 users/templates/users/user.html:29 +#: users/templates/users/user_autocapture.html:29 msgid "Users" msgstr "Utilisateurs" @@ -999,7 +1006,7 @@ msgstr "Rechercher" msgid "The following users will be archived:" msgstr "Les utilisateus suivants vont être archivés :" -#: users/templates/users/plugin_out.html:35 +#: users/templates/users/plugin_out.html:34 msgid "" "Your machine and your room were successfully registered. Please disconnect " "and reconnect your Ethernet cable to benefit from a wired connection." @@ -1274,33 +1281,33 @@ msgstr "Renvoyer l'email de confirmation ?" msgid "The confirmation email will be sent to" msgstr "Le mail de confirmation sera envoyé à" -#: users/templates/users/user.html:45 +#: users/templates/users/user.html:44 msgid "Summary of the General Terms of Use" msgstr "Résumé des Conditions Générales d'Utilisation" -#: users/templates/users/user_autocapture.html:35 +#: users/templates/users/user_autocapture.html:34 msgid "Device and room register form" msgstr "Enregistrement de votre chambre et machine fixe" -#: users/templates/users/user_autocapture.html:44 +#: users/templates/users/user_autocapture.html:43 msgid "Connected from:" msgstr "Connecté depuis :" -#: users/templates/users/user_autocapture.html:46 +#: users/templates/users/user_autocapture.html:45 #, python-format msgid "Room %(room)s" msgstr "Chambre %(room)s" -#: users/templates/users/user_autocapture.html:47 +#: users/templates/users/user_autocapture.html:46 #, python-format msgid "Port %(port)s" msgstr "Port %(port)s" -#: users/templates/users/user_autocapture.html:54 +#: users/templates/users/user_autocapture.html:53 msgid "Connected with device:" msgstr "Connecté avec l'appareil :" -#: users/templates/users/user_autocapture.html:56 +#: users/templates/users/user_autocapture.html:55 #, python-format msgid "MAC address %(mac)s" msgstr "Adresse MAC %(mac)s" From ce6f46b2daa7d583a9a8d948ada951da496e93c2 Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 24 Jan 2021 12:00:28 +0100 Subject: [PATCH 481/490] feat(trans): Translate remaining fuzzy translations --- cotisations/locale/fr/LC_MESSAGES/django.po | 4 +--- machines/locale/fr/LC_MESSAGES/django.po | 4 +--- re2o/locale/fr/LC_MESSAGES/django.po | 10 ++++------ users/locale/fr/LC_MESSAGES/django.po | 12 +++--------- 4 files changed, 9 insertions(+), 21 deletions(-) diff --git a/cotisations/locale/fr/LC_MESSAGES/django.po b/cotisations/locale/fr/LC_MESSAGES/django.po index 004cd5f4..e248ff65 100644 --- a/cotisations/locale/fr/LC_MESSAGES/django.po +++ b/cotisations/locale/fr/LC_MESSAGES/django.po @@ -131,10 +131,8 @@ msgid "date" msgstr "date" #: cotisations/models.py:66 -#, fuzzy -#| msgid "Can view an invoice object" msgid "Can view an base invoice object" -msgstr "Peut voir un objet facture" +msgstr "Peut voir un objet BaseInvoice" #: cotisations/models.py:148 msgid "cheque number" diff --git a/machines/locale/fr/LC_MESSAGES/django.po b/machines/locale/fr/LC_MESSAGES/django.po index efc5c09b..7d528ca5 100644 --- a/machines/locale/fr/LC_MESSAGES/django.po +++ b/machines/locale/fr/LC_MESSAGES/django.po @@ -418,10 +418,8 @@ msgid "You don't have the right to use all extensions." msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." #: machines/models.py:1006 -#, fuzzy -#| msgid "You don't have the right to use all extensions." msgid "You don't have the right to list all extensions." -msgstr "Vous n'avez pas le droit d'utiliser toutes les extensions." +msgstr "Vous n'avez pas le droit de voir la liste des extensions." #: machines/models.py:1016 msgid "An extension must begin with a dot." diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index ac72912b..e91db2e4 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -100,16 +100,14 @@ msgid "You don't have the right to view every %s object." msgstr "Vous n'avez pas le droit de voir tous les objets %s." #: re2o/mixins.py:207 -#, fuzzy, python-format -#| msgid "You don't have the right to view every %s object." +#, python-format msgid "You don't have the right to edit every %s object." -msgstr "Vous n'avez pas le droit de voir tous les objets %s." +msgstr "Vous n'avez pas le droit de modifier tous les objets %s." #: re2o/mixins.py:228 -#, fuzzy, python-format -#| msgid "You don't have the right to view every %s object." +#, python-format msgid "You don't have the right to list every %s object." -msgstr "Vous n'avez pas le droit de voir tous les objets %s." +msgstr "Vous n'avez pas le droit de voir la liste des objets %s." #: re2o/mixins.py:250 #, python-format diff --git a/users/locale/fr/LC_MESSAGES/django.po b/users/locale/fr/LC_MESSAGES/django.po index 20fba98d..c8d5f844 100644 --- a/users/locale/fr/LC_MESSAGES/django.po +++ b/users/locale/fr/LC_MESSAGES/django.po @@ -530,10 +530,8 @@ msgid "You don't have the right to create a user." msgstr "Vous n'avez pas le droit de créer un utilisateur." #: users/models.py:1979 -#, fuzzy -#| msgid "You don't have the right to edit another user." msgid "You don't have the right to list all adherents." -msgstr "Vous n'avez pas le droit de modifier un autre utilisateur." +msgstr "Vous n'avez pas le droit de voir la liste des adhérents." #: users/models.py:2021 msgid "club" @@ -907,10 +905,8 @@ msgstr "" "( %(objet)s ) ?" #: users/templates/users/index.html:35 -#, fuzzy -#| msgid "Add a shell" msgid "Add a user" -msgstr "Ajouter une interface en ligne de commande" +msgstr "Ajouter un utilisateur" #: users/templates/users/index_ban.html:32 #: users/templates/users/profil.html:428 @@ -922,10 +918,8 @@ msgid "Clubs" msgstr "Clubs" #: users/templates/users/index_clubs.html:35 -#, fuzzy -#| msgid "Add a ban" msgid "Add a club" -msgstr "Ajouter un bannissement" +msgstr "Ajouter un club" #: users/templates/users/index_emailaddress.html:32 msgid "Local email accounts" From 3d365a483027a053feb1524e9faff5f1c28a98b1 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Sun, 24 Jan 2021 14:36:21 +0100 Subject: [PATCH 482/490] Fix: Use local bootstrap static files --- re2o/settings.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/re2o/settings.py b/re2o/settings.py index 2e3d75a7..612741fb 100644 --- a/re2o/settings.py +++ b/re2o/settings.py @@ -165,6 +165,8 @@ if "LOCAL_ROUTERS" in globals(): # django-bootstrap3 config BOOTSTRAP3 = { + "css_url": "/javascript/bootstrap/css/bootstrap.min.css", + "javascript_url": "/javascript/bootstrap/js/bootstrap.min.js", "jquery_url": "/javascript/jquery/jquery.min.js", "base_url": "/javascript/bootstrap/", "include_jquery": True, @@ -176,6 +178,7 @@ BOOTSTRAP_BASE_URL = "/javascript/bootstrap/" STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static").replace("\\", "/"), "/usr/share/fonts-font-awesome/", + "/usr/share/javascript/", ) # Directory where the static files served by the server are stored STATIC_ROOT = os.path.join(BASE_DIR, "static_files") From 77b599ad347d5d41f2241edd3c082b0bf744bbdf Mon Sep 17 00:00:00 2001 From: chirac Date: Sun, 24 Jan 2021 14:19:42 +0100 Subject: [PATCH 483/490] fix: the function all_ap_in doesn't crash anymore on dormitory use --- topologie/models.py | 12 ++++++------ topologie/views.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/topologie/models.py b/topologie/models.py index b7219d27..faecf380 100644 --- a/topologie/models.py +++ b/topologie/models.py @@ -129,17 +129,17 @@ class AccessPoint(Machine): return str(self.interface_set.first().domain.name) @classmethod - def all_ap_in(cls, building_instance): - """Get all the APs of the given building. + def all_ap_in(cls, building_set): + """Get all the APs of the given building set. Args: - building_instance: the building used to find APs. + building_set: the building set used to find APs. Returns: - The queryset of all APs in the given building. + The queryset of all APs in the given building set. """ return cls.objects.filter( - interface__port__switch__switchbay__building=building_instance + interface__port__switch__switchbay__building__in=building_set ) def __str__(self): @@ -770,7 +770,7 @@ class Building(AclMixin, RevMixin, models.Model): def all_ap_in(self): """Get all the APs in the building.""" - return AccessPoint.all_ap_in(self) + return AccessPoint.all_ap_in(Building.objects.filter(id=self.id)) def get_name(self): if Dormitory.is_multiple_dorms(): diff --git a/topologie/views.py b/topologie/views.py index 93c21d0e..cddfa28e 100644 --- a/topologie/views.py +++ b/topologie/views.py @@ -1331,7 +1331,7 @@ def make_machine_graph(): {"numero": port.port, "related": port.related.switch.get_name} ) - for ap in AccessPoint.all_ap_in(building).prefetch_related( + for ap in building.all_ap_in().prefetch_related( Prefetch( "interface_set", queryset=( From 59b948f98846f45cfef0c281c8d0c6d481dd1a6b Mon Sep 17 00:00:00 2001 From: chirac Date: Sun, 24 Jan 2021 15:07:28 +0100 Subject: [PATCH 484/490] fix: Error on rezometz website --- re2o/locale/fr/LC_MESSAGES/django.po | 4 ++-- re2o/templates/re2o/about.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/re2o/locale/fr/LC_MESSAGES/django.po b/re2o/locale/fr/LC_MESSAGES/django.po index e91db2e4..6c6f4422 100644 --- a/re2o/locale/fr/LC_MESSAGES/django.po +++ b/re2o/locale/fr/LC_MESSAGES/django.po @@ -160,7 +160,7 @@ msgstr "Informations supplémentaires" #: re2o/templates/re2o/about.html:57 msgid "" -"Re2o is an administration tool initiated by Rezo Metz and a few members of other FedeRez associations around the summer 2016.
    It is intended to " "be a tool independent from any network infrastructure so it can be setup in " @@ -171,7 +171,7 @@ msgid "" "process, we will be glad to welcome you so do not hesitate to contact us and " "come help us build the future of Re2o." msgstr "" -"Re2o est un outil d'administration initié par Rézo Metz et quelques membres d'autres associations de FedeRez autour de l'été 2016.
    Il se veut " "être un outil indépendant de toute infrastructure réseau pour pouvoir être " diff --git a/re2o/templates/re2o/about.html b/re2o/templates/re2o/about.html index cbe48a13..b74d7e81 100644 --- a/re2o/templates/re2o/about.html +++ b/re2o/templates/re2o/about.html @@ -56,7 +56,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,

    {% trans "About Re2o" %}

    {% blocktrans trimmed %} Re2o is an administration tool initiated by - Rezo Metz and a few + Rezo Metz and a few members of other FedeRez associations around the summer 2016.
    It is intended to be a tool independent from any network infrastructure From 9abd329e161004c4ca8fd7fd6cd0d15c2ab8050d Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Sun, 24 Jan 2021 16:17:19 +0100 Subject: [PATCH 485/490] fix: Indentation and variable name issues in users/models.py --- users/models.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/users/models.py b/users/models.py index 8313bfd7..befcd8bc 100755 --- a/users/models.py +++ b/users/models.py @@ -92,8 +92,10 @@ from PIL import Image from io import BytesIO import sys + # General utilities + def linux_user_check(login): """Check if a login comply with unix base login policy @@ -913,8 +915,8 @@ class User( days_not_zero = self.facture_set.filter(valid=True).exclude(Q(vente__duration_days_membership=0)).exists() if(not_zero or days_not_zero\ or OptionalUser.get_cached_value("all_users_active")): - self.state = self.STATE_ACTIVE - self.save() + self.state = self.STATE_ACTIVE + self.save() if self.state == self.STATE_ARCHIVE or self.state == self.STATE_FULL_ARCHIVE: self.unarchive() self.state = self.STATE_ACTIVE @@ -1862,6 +1864,7 @@ class User( """ return self.theme.split(".")[0] + class Adherent(User): """Base re2o Adherent model, inherit from User. Add other attributes. @@ -1923,7 +1926,7 @@ class Adherent(User): :return: An adherent. """ - return cls.objects.get(pk=adherentid) + return cls.objects.get(pk=object_id) @staticmethod def can_create(user_request, *_args, **_kwargs): @@ -2790,4 +2793,3 @@ class EMailAddress(RevMixin, AclMixin, models.Model): if result: raise ValidationError(reason) super(EMailAddress, self).clean(*args, **kwargs) - From db929676e2b870ccf83d0a504a7c8719f53a8fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Pi=C3=A9tri?= Date: Wed, 13 Jan 2021 15:31:29 +0100 Subject: [PATCH 486/490] fix: :bug: Fix voucher_pdf function (#315) Replace date_end by date_end_memb in voucher_pdf. Close #315 --- cotisations/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cotisations/views.py b/cotisations/views.py index 35fdc82a..e114b569 100644 --- a/cotisations/views.py +++ b/cotisations/views.py @@ -1057,7 +1057,7 @@ def voucher_pdf(request, invoice, **_kwargs): "lastname": invoice.user.surname, "email": invoice.user.email, "phone": invoice.user.telephone, - "date_end": invoice.get_subscription().latest("date_end").date_end, + "date_end": invoice.get_subscription().latest("date_end_memb").date_end_memb, "date_begin": invoice.date, }, ) From 9d33031bb6327175b8ecc9a2b25f20491beeb48c Mon Sep 17 00:00:00 2001 From: Jean-Romain Garnier Date: Fri, 15 Jan 2021 18:55:46 +0100 Subject: [PATCH 487/490] fix: Potential information leak in serviceuser history --- logs/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logs/models.py b/logs/models.py index aa5d7822..51b250f3 100644 --- a/logs/models.py +++ b/logs/models.py @@ -360,7 +360,7 @@ class HistoryEvent: return value - def edits(self, hide=[]): + def edits(self, hide=["password", "pwd_ntlm"]): """Get the list of the changes performed during this event. Args: From 0287d7aa0c64d7b3b9f8eb471407831451e6bdeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yoann=20Pi=C3=A9tri?= Date: Sun, 17 Jan 2021 19:32:05 +0100 Subject: [PATCH 488/490] fix: :pencil2: Fix typo in navbar --- templates/locale/fr/LC_MESSAGES/django.po | 2 +- templates/nav.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 4909dd5d..1cba3a5d 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -270,7 +270,7 @@ msgid "Control invoices" msgstr "Contrôler les factures" #: templates/nav.html:112 -msgid "Cutsom invoices" +msgid "Custom invoices" msgstr "Facture personnalisée" #: templates/nav.html:117 diff --git a/templates/nav.html b/templates/nav.html index c7cc394f..5ffe4f09 100644 --- a/templates/nav.html +++ b/templates/nav.html @@ -109,7 +109,7 @@ with this program; if not, write to the Free Software Foundation, Inc., {% trans "Invoices" %}

  • - {% trans "Cutsom invoices" %}
  • + {% trans "Custom invoices" %}
  • Date: Sun, 24 Jan 2021 10:51:46 +0100 Subject: [PATCH 489/490] fix: typo in french translation of "home" --- templates/locale/fr/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/locale/fr/LC_MESSAGES/django.po b/templates/locale/fr/LC_MESSAGES/django.po index 1cba3a5d..00078d6e 100644 --- a/templates/locale/fr/LC_MESSAGES/django.po +++ b/templates/locale/fr/LC_MESSAGES/django.po @@ -88,7 +88,7 @@ msgstr "Contenu incconu" #: templates/registration/password_reset_done.html:11 #: templates/registration/password_reset_form.html:11 msgid "Home" -msgstr "Acceuil" +msgstr "Accueil" #: templates/buttons/add.html:27 msgid "Add" From 0f7c841403ff380e643c8d8f19d72c22028a93e1 Mon Sep 17 00:00:00 2001 From: jr-garnier Date: Sun, 24 Jan 2021 15:25:46 +0100 Subject: [PATCH 490/490] fix: Links in CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88a30368..405fb670 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,13 +57,13 @@ Here is a list of noteworthy features brought by this update: * [!516](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Detailed events in history views (e.g. show `old_email -> new_email`). * [!519](https://gitlab.federez.net/re2o/re2o/-/merge_requests/519): Add ability to filter event logs (e.g. to show all the subscriptions added by an admin). * [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Refactor navbar to make menu navigation easier. -* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Add ability to install custom themes. +* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Add ability to install custom themes (checkout [this repository](https://gitlab.federez.net/re2o/re2o-themes) for a list of Re2o themes). * [!578](https://gitlab.federez.net/re2o/re2o/-/merge_requests/578) : Migrations squashed to ease the installation process. * [!582](https://gitlab.federez.net/re2o/re2o/-/merge_requests/582): Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account). * [!589](https://gitlab.federez.net/re2o/re2o/-/merge_requests/589): Move LDAP to a separate optional app. * Plenty of bug fixes. -You can view the full list of closed issues [here](https://gitlab.federez.net/re2o/re2o/-/issues?scope=all&state=all&milestone_title=Re2o 2.9). +You can view the full list of closed issues [here](https://gitlab.federez.net/re2o/re2o/-/issues?scope=all&state=all&milestone_title=Re2o%202.9). # Before Re2o 2.9