8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-22 03:13:12 +00:00

Merge branch 'docstrings_in_english' into 'dev'

Docstring translation

See merge request re2o/re2o!520
This commit is contained in:
klafyvel 2020-06-01 21:49:14 +02:00
commit d51da9ddfa
45 changed files with 4553 additions and 1817 deletions

View file

@ -34,7 +34,7 @@ from .models import CustomInvoice, CostEstimate
class FactureAdmin(VersionAdmin):
"""Class admin d'une facture, tous les champs"""
"""Admin class for invoices."""
pass
@ -52,32 +52,31 @@ class CustomInvoiceAdmin(VersionAdmin):
class VenteAdmin(VersionAdmin):
"""Class admin d'une vente, tous les champs (facture related)"""
"""Admin class for purchases."""
pass
class ArticleAdmin(VersionAdmin):
"""Class admin d'un article en vente"""
"""Admin class for articles."""
pass
class BanqueAdmin(VersionAdmin):
"""Class admin de la liste des banques (facture related)"""
"""Admin class for banks."""
pass
class PaiementAdmin(VersionAdmin):
"""Class admin d'un moyen de paiement (facture related"""
"""Admin class for payment methods."""
pass
class CotisationAdmin(VersionAdmin):
"""Class admin d'une cotisation (date de debut et de fin),
Vente related"""
"""Admin class for subscriptions."""
pass

View file

@ -185,7 +185,7 @@ def new_cost_estimate(request):
"""
# The template needs the list of articles (for the JS part)
articles = Article.objects.all()
# Building the invocie form and the article formset
# Building the invoice form and the article formset
cost_estimate_form = CostEstimateForm(request.POST or None)
articles_formset = formset_factory(SelectArticleForm)(
@ -240,7 +240,7 @@ def new_custom_invoice(request):
"""
# The template needs the list of articles (for the JS part)
articles = Article.objects.all()
# Building the invocie form and the article formset
# Building the invoice form and the article formset
invoice_form = CustomInvoiceForm(request.POST or None)
articles_formset = formset_factory(SelectArticleForm)(
@ -366,7 +366,7 @@ def edit_facture(request, facture, **_kwargs):
@can_delete(Facture)
def del_facture(request, facture, **_kwargs):
"""
View used to delete an existing invocie.
View used to delete an existing invoice.
"""
if request.method == "POST":
facture.delete()
@ -382,7 +382,7 @@ def del_facture(request, facture, **_kwargs):
@login_required
@can_edit(CostEstimate)
def edit_cost_estimate(request, invoice, **kwargs):
# Building the invocie form and the article formset
# Building the invoice form and the article formset
invoice_form = CostEstimateForm(request.POST or None, instance=invoice)
purchases_objects = Vente.objects.filter(facture=invoice)
purchase_form_set = modelformset_factory(
@ -411,7 +411,7 @@ def edit_cost_estimate(request, invoice, **kwargs):
@can_edit(CostEstimate)
@can_create(CustomInvoice)
def cost_estimate_to_invoice(request, cost_estimate, **_kwargs):
"""Create a custom invoice from a cos estimate"""
"""Create a custom invoice from a cost estimate."""
cost_estimate.create_invoice()
messages.success(
request, _("An invoice was successfully created from your cost estimate.")
@ -422,7 +422,7 @@ def cost_estimate_to_invoice(request, cost_estimate, **_kwargs):
@login_required
@can_edit(CustomInvoice)
def edit_custom_invoice(request, invoice, **kwargs):
# Building the invocie form and the article formset
# Building the invoice form and the article formset
invoice_form = CustomInvoiceForm(request.POST or None, instance=invoice)
purchases_objects = Vente.objects.filter(facture=invoice)
purchase_form_set = modelformset_factory(
@ -498,7 +498,7 @@ def cost_estimate_pdf(request, invoice, **_kwargs):
@can_delete(CostEstimate)
def del_cost_estimate(request, estimate, **_kwargs):
"""
View used to delete an existing invocie.
View used to delete an existing invoice.
"""
if request.method == "POST":
estimate.delete()
@ -561,7 +561,7 @@ def custom_invoice_pdf(request, invoice, **_kwargs):
@can_delete(CustomInvoice)
def del_custom_invoice(request, invoice, **_kwargs):
"""
View used to delete an existing invocie.
View used to delete an existing invoice.
"""
if request.method == "POST":
invoice.delete()

View file

@ -53,6 +53,15 @@ CHOICES_TYPE = (
def all_classes(module):
"""Get the list of all class names of the module.
Args:
module: the module in which to retrieve classes.
Returns:
A list containing the names of all classes that are defined in
the module.
"""
classes = []
for name, obj in inspect.getmembers(module):
@ -63,8 +72,16 @@ def all_classes(module):
def classes_for_action_type(action_type):
"""Return the list of class names to be displayed for a
given actions type filter"""
"""Get the list of class names to be displayed for a given action type
filter.
Args:
action_type: the string used to filter the class names.
Returns:
A list containing the class names corresponding to the action type
filter.
"""
if action_type == "users":
return [
users.models.User.__name__,
@ -96,7 +113,7 @@ def classes_for_action_type(action_type):
class ActionsSearchForm(Form):
"""The form for a simple search"""
"""Form used to do an advanced search through the logs."""
u = forms.ModelChoiceField(
label=_("Performed by"),
queryset=users.models.User.objects.all(),
@ -123,7 +140,7 @@ class ActionsSearchForm(Form):
class MachineHistorySearchForm(Form):
"""The form for a simple search"""
"""Form used to do a search through the machine histories."""
q = forms.CharField(
label=_("Search"),
max_length=100,

View file

@ -69,12 +69,16 @@ def make_version_filter(key, value):
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
: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
"""Initialise an instance of MachineHistorySearchEvent.
Args:
user: User, the user owning the machine at the time of the event.
machine: Version, the machine version related to the interface.
interface: Version, the interface targeted by this event.
start: datetime, the date at which this version was created
(default: None).
end: datetime, the date at which this version was replace by a new
one (default: None).
"""
self.user = user
self.machine = machine
@ -86,9 +90,13 @@ class MachineHistorySearchEvent:
self.comment = interface.revision.get_comment() or None
def is_similar(self, elt2):
"""
Checks whether two events are similar enough to be merged
:return: bool
"""Check whether two events are similar enough to be merged.
Args:
elt2: MachineHistorySearchEvent, the event to compare with self.
Returns:
A boolean, True if the events can be merged and False otherwise.
"""
return (
elt2 is not None
@ -115,10 +123,14 @@ class MachineHistorySearch:
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 MachineHistorySearchEvent in reverse chronological order
"""Get the events in machine histories related to the search.
Args:
search: the IP or MAC address used in the search.
params: the dictionary built by the search view.
Returns:
A list of MachineHistorySearchEvent in reverse chronological order.
"""
self.start = params.get("s", None)
self.end = params.get("e", None)
@ -140,11 +152,12 @@ class MachineHistorySearch:
return []
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
"""Add a new revision to the chronological order.
Args:
user: User, the user owning the maching at the time of the event.
machine: Version, the machine version related to the interface.
interface: Version, the interface targeted by this event.
"""
evt = MachineHistorySearchEvent(user, machine, interface)
evt.start_date = interface.revision.date_created
@ -171,10 +184,15 @@ class MachineHistorySearch:
self._last_evt = evt
def _get_interfaces_for_ip(self, ip):
"""
:param ip: str
:return: An iterable object with the Version objects
of Interfaces with the given IP
"""Get the Version objects of interfaces with the given IP
address.
Args:
ip: the string corresponding to the IP address.
Returns:
An iterable object with the Version objects of interfaces with the
given IP address.
"""
# TODO: What if ip list was deleted?
try:
@ -189,10 +207,15 @@ class MachineHistorySearch:
)
def _get_interfaces_for_mac(self, mac):
"""
:param mac: str
:return: An iterable object with the Version objects
of Interfaces with the given MAC address
"""Get the Version objects of interfaces with the given MAC
address.
Args:
mac: the string corresponding to the MAC address.
Returns:
An iterable object with the Version objects of interfaces with the
given MAC address.
"""
return (
Version.objects.get_for_model(Interface)
@ -201,10 +224,14 @@ class MachineHistorySearch:
)
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
which the given interface was attributed
"""Get the Version objects of machines with the given interface.
Args:
interface: Version, the interface used to find machines.
Returns:
An iterable object with the Version objects of machines to which
the given interface was assigned.
"""
machine_id = interface.field_dict["machine_id"]
return (
@ -214,18 +241,27 @@ class MachineHistorySearch:
)
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
"""Get the User instance owning the given machine.
Args:
machine: Version, the machine used to find its owner.
Returns:
The User instance of the owner of the given machine.
"""
# 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):
"""
:param ip: str, The IP to lookup
:returns: list, a list of MachineHistorySearchEvent
"""Get events related to the given IP address.
Args:
ip: the string corresponding to the IP address.
Returns:
A list of MachineHistorySearchEvent related to the given IP
address.
"""
interfaces = self._get_interfaces_for_ip(ip)
@ -239,9 +275,14 @@ class MachineHistorySearch:
return self.events
def _get_by_mac(self, mac):
"""
:param mac: str, The MAC address to lookup
:returns: list, a list of MachineHistorySearchEvent
"""Get events related to the given MAC address.
Args:
mac: the string corresponding to the MAC address.
Returns:
A list of MachineHistorySearchEvent related to the given MAC
address.
"""
interfaces = self._get_interfaces_for_mac(mac)
@ -261,10 +302,10 @@ class MachineHistorySearch:
class RelatedHistory:
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
"""Initialise an instance of RelatedHistory.
Args:
version: Version, the version related to the history.
"""
self.version = version
self.app_name = version.content_type.app_label
@ -287,10 +328,14 @@ class RelatedHistory:
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
"""Initialise an instance of HistoryEvent.
Args:
version: Version, the version of the object for this event.
previous_version: Version, the version of the object before this
event (default: None).
edited_fields: list, The list of modified fields by this event
(default: None).
"""
self.version = version
self.previous_version = previous_version
@ -300,11 +345,15 @@ class HistoryEvent:
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
"""Get the appropriate representation of the given field.
Args:
name: the name of the field
value: the value of the field
Returns:
The string corresponding to the appropriate representation of the
given field.
"""
if value is None:
return _("None")
@ -312,10 +361,14 @@ class HistoryEvent:
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
"""Get the list of the changes performed during this event.
Args:
hide: the list of fields for which not to show details (default:
[]).
Returns:
The list of fields edited by the event to display.
"""
edits = []
@ -342,10 +395,15 @@ class History:
self.event_type = HistoryEvent
def get(self, instance_id, model):
"""
: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
"""Get the list of history events of the given object.
Args:
instance_id: int, the id of the instance to lookup.
model: class, the type of object to lookup.
Returns:
A list of HistoryEvent, in reverse chronological order, related to
the given object or None if no version was found.
"""
self.events = []
@ -368,12 +426,16 @@ class History:
return self.events[::-1]
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
"""Find the edited fields between two versions.
Args:
v1: Version to compare.
v2: Version to compare.
ignoring: a list of fields to ignore.
Returns:
The list of field names in v1 that are different from the ones in
v2.
"""
fields = []
@ -384,9 +446,10 @@ 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
"""Add a new revision to the chronological order.
Args:
version: Version, the version of the interface for this event.
"""
diff = None
if self._last_version is not None:
@ -427,6 +490,15 @@ class VersionAction(HistoryEvent):
return apps.get_model(self.application(), self.model_name())
def edits(self, hide=["password", "pwd_ntlm", "gpg_fingerprint"]):
"""Get the list of the changes performed during this event.
Args:
hide: the list of fields for which not to show details (default:
["password", "pwd_ntlm", "gpg_fingerprint"]).
Returns:
The list of fields edited by the event to display.
"""
self.previous_version = self._previous_version()
if self.previous_version is None:
@ -436,6 +508,12 @@ class VersionAction(HistoryEvent):
return super(VersionAction, self).edits(hide)
def _previous_version(self):
"""Get the previous version of self.
Returns:
The Version corresponding to the previous version of self, or None
in case of exception.
"""
model = self.object_type()
try:
query = (
@ -451,12 +529,16 @@ class VersionAction(HistoryEvent):
return None
def _compute_diff(self, v1, v2, ignoring=["pwd_ntlm"]):
"""
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
"""Find the edited fields between two versions.
Args:
v1: Version to compare.
v2: Version to compare.
ignoring: a list of fields to ignore (default: ["pwd_ntlm"]).
Returns:
The list of field names in v1 that are different from the ones in
v2.
"""
fields = []
@ -468,7 +550,8 @@ class VersionAction(HistoryEvent):
class RevisionAction:
"""A Revision may group multiple Version objects together"""
"""A Revision may group multiple Version objects together."""
def __init__(self, revision):
self.performed_by = revision.user
self.revision = revision
@ -486,9 +569,13 @@ class RevisionAction:
class ActionsSearch:
def get(self, params):
"""
:param params: dict built by the search view
:return: QuerySet of Revision objects
"""Get the Revision objects corresponding to the search.
Args:
params: dictionary built by the search view.
Returns:
The QuerySet of Revision objects corresponding to the search.
"""
user = params.get("u", None)
start = params.get("s", None)
@ -540,11 +627,15 @@ class ActionsSearch:
class UserHistoryEvent(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
"""Get the appropriate representation of the given field.
Args:
name: the name of the field
value: the value of the field
Returns:
The string corresponding to the appropriate representation of the
given field.
"""
if name == "groups":
if len(value) == 0:
@ -599,10 +690,14 @@ class UserHistoryEvent(HistoryEvent):
return super(UserHistoryEvent, self)._repr(name, value)
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
:return: str
"""Get the list of the changes performed during this event.
Args:
hide: the list of fields for which not to show details (default:
["password", "pwd_ntlm", "gpg_fingerprint"]).
Returns:
The list of fields edited by the event to display.
"""
return super(UserHistoryEvent, self).edits(hide)
@ -631,9 +726,14 @@ class UserHistory(History):
self.event_type = UserHistoryEvent
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
"""Get the the list of UserHistoryEvent related to the object.
Args:
user_id: int, the id of the user to lookup.
Returns:
The list of UserHistoryEvent, in reverse chronological order,
related to the object, or None if nothing was found.
"""
self.events = []
@ -710,10 +810,10 @@ class UserHistory(History):
)
def _add_revision(self, 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
"""Add a new revision to the chronological order.
Args:
version: Version, the version of the user for this event.
"""
diff = None
if self._last_version is not None:
@ -736,11 +836,15 @@ class UserHistory(History):
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
"""Return the appropriate representation of the given field.
Args:
name: the name of the field
value: the value of the field
Returns:
The string corresponding to the appropriate representation of the
given field.
"""
if name == "user_id":
try:
@ -757,6 +861,15 @@ class MachineHistory(History):
self.event_type = MachineHistoryEvent
def get(self, machine_id, model):
"""Get the the list of MachineHistoryEvent related to the object.
Args:
machine_id: int, the id of the machine to lookup.
Returns:
The list of MachineHistoryEvent, in reverse chronological order,
related to the object.
"""
self.related = (
Version.objects.get_for_model(Interface)
.filter(make_version_filter("machine", machine_id))
@ -772,11 +885,15 @@ class MachineHistory(History):
class InterfaceHistoryEvent(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
"""Get the appropriate representation of the given field.
Args:
name: the name of the field
value: the value of the field
Returns:
The string corresponding to the appropriate representation of the
given field.
"""
if name == "ipv4_id" and value is not None:
try:
@ -813,6 +930,15 @@ class InterfaceHistory(History):
self.event_type = InterfaceHistoryEvent
def get(self, interface_id, model):
"""Get the the list of InterfaceHistoryEvent related to the object.
Args:
interface_id: int, the id of the interface to lookup.
Returns:
The list of InterfaceHistoryEvent, in reverse chronological order,
related to the object.
"""
return super(InterfaceHistory, self).get(interface_id, Interface)
@ -829,10 +955,15 @@ HISTORY_CLASS_MAPPING = {
def get_history_class(model):
"""
Find the mos appropriate History subclass to represent
the given model's history
:model: class
"""Get the most appropriate History subclass to represent the given model's
history.
Args:
model: the class for which to get the history.
Returns:
The most appropriate History subclass for the given model's history,
or History if no other was found.
"""
try:
return HISTORY_CLASS_MAPPING[model]()

View file

@ -19,9 +19,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.
"""
Urls de l'application logs, pointe vers les fonctions de views.
Inclu dans le re2o.urls
"""logs.urls
The defined URLs for the logs app. Included in re2o.urls.
"""
from __future__ import unicode_literals

View file

@ -24,16 +24,16 @@
# App de gestion des statistiques pour re2o
# Gabriel Détraz
# Gplv2
"""
Vues des logs et statistiques générales.
"""logs.views
Views of logs and general statistics.
La vue index générale affiche une selection des dernières actions,
classées selon l'importance, avec date, et user formatés.
The general indew view displays a list of the last actions, sorted by
importance, with date and user formatted.
Stats_logs renvoie l'ensemble des logs.
stats_logs returns all the logs.
Les autres vues sont thématiques, ensemble des statistiques et du
nombre d'objets par models, nombre d'actions par user, etc
The other views are related to specific topics, with statistics for number of
objects for per model, number of actions per user etc.
"""
from __future__ import unicode_literals
@ -113,8 +113,7 @@ from .forms import ActionsSearchForm, MachineHistorySearchForm
@login_required
@can_view_app("logs")
def index(request):
"""Affiche les logs affinés, date reformatées, selectionne
les event importants (ajout de droits, ajout de ban/whitelist)"""
"""View used to display summary of events about users."""
pagination_number = GeneralOption.get_cached_value("pagination_number")
# The types of content kept for display
content_type_filter = ["ban", "whitelist", "vente", "interface", "user"]
@ -155,8 +154,7 @@ def index(request):
@login_required
@can_view_all(GeneralOption)
def stats_logs(request):
"""Affiche l'ensemble des logs et des modifications sur les objets,
classés par date croissante, en vrac"""
"""View used to do an advanced search through the logs."""
actions_form = ActionsSearchForm(request.GET or None)
if actions_form.is_valid():
@ -185,7 +183,7 @@ def stats_logs(request):
@login_required
@can_edit_history
def revert_action(request, revision_id):
""" Annule l'action en question """
"""View used to revert actions."""
try:
revision = Revision.objects.get(id=revision_id)
except Revision.DoesNotExist:
@ -204,9 +202,10 @@ def revert_action(request, revision_id):
@login_required
@can_view_all(IpList, Interface, User)
def stats_general(request):
"""Statistiques générales affinées sur les ip, activées, utilisées par
range, et les statistiques générales sur les users : users actifs,
cotisants, activés, archivés, etc"""
"""View used to display general statistics about users (activated,
disabled, archived etc.) and IP addresses (ranges, number of assigned
addresses etc.).
"""
ip_dict = dict()
for ip_range in IpType.objects.select_related("vlan").all():
all_ip = IpList.objects.filter(ip_type=ip_range)
@ -377,9 +376,9 @@ def stats_general(request):
@login_required
@can_view_app("users", "cotisations", "machines", "topologie")
def stats_models(request):
"""Statistiques générales, affiche les comptages par models:
nombre d'users, d'écoles, de droits, de bannissements,
de factures, de ventes, de banque, de machines, etc"""
"""View used to display general statistics about the number of objects
stored in the database, for each model.
"""
stats = {
_("Users (members and clubs)"): {
"users": [User._meta.verbose_name, User.objects.count()],
@ -452,10 +451,9 @@ def stats_models(request):
@login_required
@can_view_app("users")
def stats_users(request):
"""Affiche les statistiques base de données aggrégées par user :
nombre de machines par user, d'etablissements par user,
de moyens de paiements par user, de banque par user,
de bannissement par user, etc"""
"""View used to display statistics aggregated by user (number of machines,
bans, whitelists, rights etc.).
"""
stats = {
User._meta.verbose_name: {
Machine._meta.verbose_name_plural: User.objects.annotate(
@ -496,9 +494,7 @@ def stats_users(request):
@login_required
@can_view_app("users")
def stats_actions(request):
"""Vue qui affiche les statistiques de modifications d'objets par
utilisateurs.
Affiche le nombre de modifications aggrégées par utilisateurs"""
"""View used to display the number of actions, aggregated by user."""
stats = {
User._meta.verbose_name: {
_("actions"): User.objects.annotate(num=Count("revision")).order_by("-num")[
@ -512,8 +508,9 @@ def stats_actions(request):
@login_required
@can_view_app("users")
def stats_search_machine_history(request):
"""View which displays the history of machines with the given
une IP or MAC adresse"""
"""View used to display the history of machines with the given IP or MAC
address.
"""
history_form = MachineHistorySearchForm(request.GET or None)
if history_form.is_valid():
history = MachineHistorySearch()

View file

@ -22,15 +22,15 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Formulaires d'ajout, edition et suppressions de :
- machines
- interfaces
- domain (noms de machine)
- alias (cname)
- service (dhcp, dns..)
- ns (serveur dns)
- mx (serveur mail)
- ports ouverts et profils d'ouverture par interface
Forms to create, edit and delete:
* machines
* interfaces
* domains (machine names)
* aliases (CNAME)
* services (DHCP, DNS...)
* NS records (DNS server)
* MX records (mail serveur)
* open ports and ports opening for interfaces
"""
from __future__ import unicode_literals
@ -66,7 +66,7 @@ from .models import (
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Formulaire d'édition d'une machine"""
"""Form used to edit a machine."""
class Meta:
model = Machine
@ -79,14 +79,14 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class NewMachineForm(EditMachineForm):
"""Creation d'une machine, ne renseigne que le nom"""
"""Form used to create a machine."""
class Meta(EditMachineForm.Meta):
fields = ["name"]
class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Edition d'une interface. Edition complète"""
"""Form used to edit an interface."""
class Meta:
model = Interface
@ -127,15 +127,14 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class AddInterfaceForm(EditInterfaceForm):
"""Ajout d'une interface à une machine. En fonction des droits,
affiche ou non l'ensemble des ip disponibles"""
"""Form used to add an interface to a machine."""
class Meta(EditInterfaceForm.Meta):
fields = ["machine_type", "ipv4", "mac_address", "details"]
class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Ajout d'un alias (et edition), CNAME, contenant nom et extension"""
"""Form used to add and edit an alias (CNAME)."""
class Meta:
model = Domain
@ -153,7 +152,9 @@ class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class DomainForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Ajout et edition d'un enregistrement de nom, relié à interface"""
"""Form used to add and edit a domain record, related to an
interface.
"""
class Meta:
model = Domain
@ -165,7 +166,7 @@ class DomainForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class DelAliasForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs objets alias"""
"""Form used to delete one or several aliases."""
alias = forms.ModelMultipleChoiceField(
queryset=Domain.objects.all(),
@ -182,7 +183,7 @@ class DelAliasForm(FormRevMixin, Form):
class MachineTypeForm(FormRevMixin, ModelForm):
"""Ajout et edition d'un machinetype, relié à un iptype"""
"""Form used to add and edit a machine type, related to an IP type."""
class Meta:
model = MachineType
@ -196,7 +197,7 @@ class MachineTypeForm(FormRevMixin, ModelForm):
class DelMachineTypeForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs machinetype"""
"""Form used to delete one or several machines types."""
machinetypes = forms.ModelMultipleChoiceField(
queryset=MachineType.objects.none(),
@ -214,8 +215,9 @@ class DelMachineTypeForm(FormRevMixin, Form):
class IpTypeForm(FormRevMixin, ModelForm):
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de
stop après creation"""
"""Form used to add an IP type. The start and stop IP addresses cannot
be changed afterwards.
"""
class Meta:
model = IpType
@ -228,8 +230,9 @@ class IpTypeForm(FormRevMixin, ModelForm):
class EditIpTypeForm(IpTypeForm):
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait
synchroniser les objets iplist"""
"""Form used to edit an IP type. The start and stop IP addresses cannot
be changed.
"""
class Meta(IpTypeForm.Meta):
fields = [
@ -248,7 +251,7 @@ class EditIpTypeForm(IpTypeForm):
class DelIpTypeForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs iptype"""
"""Form used to delete one or several IP types."""
iptypes = forms.ModelMultipleChoiceField(
queryset=IpType.objects.none(),
@ -266,7 +269,7 @@ class DelIpTypeForm(FormRevMixin, Form):
class ExtensionForm(FormRevMixin, ModelForm):
"""Formulaire d'ajout et edition d'une extension"""
"""Form used to add and edit extensions."""
class Meta:
model = Extension
@ -283,7 +286,7 @@ class ExtensionForm(FormRevMixin, ModelForm):
class DelExtensionForm(FormRevMixin, Form):
"""Suppression d'une ou plusieurs extensions"""
"""Form used to delete one or several extensions."""
extensions = forms.ModelMultipleChoiceField(
queryset=Extension.objects.none(),
@ -301,7 +304,7 @@ class DelExtensionForm(FormRevMixin, Form):
class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Gestion des ipv6 d'une machine"""
"""Form used to manage lists of IPv6 addresses."""
class Meta:
model = Ipv6List
@ -313,7 +316,7 @@ class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class SOAForm(FormRevMixin, ModelForm):
"""Ajout et edition d'un SOA"""
"""Form used to add and edit SOA records."""
class Meta:
model = SOA
@ -325,7 +328,7 @@ class SOAForm(FormRevMixin, ModelForm):
class DelSOAForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs SOA"""
"""Form used to delete one or several SOA records."""
soa = forms.ModelMultipleChoiceField(
queryset=SOA.objects.none(),
@ -343,7 +346,7 @@ class DelSOAForm(FormRevMixin, Form):
class MxForm(FormRevMixin, ModelForm):
"""Ajout et edition d'un MX"""
"""Form used to add and edit MX records."""
class Meta:
model = Mx
@ -358,7 +361,7 @@ class MxForm(FormRevMixin, ModelForm):
class DelMxForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs MX"""
"""Form used to delete one or several MX records."""
mx = forms.ModelMultipleChoiceField(
queryset=Mx.objects.none(),
@ -376,9 +379,9 @@ class DelMxForm(FormRevMixin, Form):
class NsForm(FormRevMixin, ModelForm):
"""Ajout d'un NS pour une zone
On exclue les CNAME dans les objets domain (interdit par la rfc)
donc on prend uniquemet """
"""Form used to add and edit NS records. Only interface names are
available because CNAME aliases should not be used in the records.
"""
class Meta:
model = Ns
@ -393,7 +396,7 @@ class NsForm(FormRevMixin, ModelForm):
class DelNsForm(FormRevMixin, Form):
"""Suppresion d'un ou plusieurs NS"""
"""Form used to delete one or several NS records."""
nss = forms.ModelMultipleChoiceField(
queryset=Ns.objects.none(),
@ -411,7 +414,7 @@ class DelNsForm(FormRevMixin, Form):
class TxtForm(FormRevMixin, ModelForm):
"""Ajout d'un txt pour une zone"""
"""Form used to add and edit TXT records."""
class Meta:
model = Txt
@ -423,7 +426,7 @@ class TxtForm(FormRevMixin, ModelForm):
class DelTxtForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs TXT"""
"""Form used to delete one or several TXT records."""
txt = forms.ModelMultipleChoiceField(
queryset=Txt.objects.none(),
@ -441,7 +444,7 @@ class DelTxtForm(FormRevMixin, Form):
class DNameForm(FormRevMixin, ModelForm):
"""Add a DNAME entry for a zone"""
"""Form used to add and edit DNAME records."""
class Meta:
model = DName
@ -453,7 +456,7 @@ class DNameForm(FormRevMixin, ModelForm):
class DelDNameForm(FormRevMixin, Form):
"""Delete a set of DNAME entries"""
"""Form used to delete one or several DNAME records."""
dnames = forms.ModelMultipleChoiceField(
queryset=Txt.objects.none(),
@ -471,7 +474,7 @@ class DelDNameForm(FormRevMixin, Form):
class SrvForm(FormRevMixin, ModelForm):
"""Ajout d'un srv pour une zone"""
"""Form used to add and edit SRV records."""
class Meta:
model = Srv
@ -483,7 +486,7 @@ class SrvForm(FormRevMixin, ModelForm):
class DelSrvForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs Srv"""
"""Form used to delete one or several SRV records."""
srv = forms.ModelMultipleChoiceField(
queryset=Srv.objects.none(),
@ -501,8 +504,7 @@ class DelSrvForm(FormRevMixin, Form):
class NasForm(FormRevMixin, ModelForm):
"""Ajout d'un type de nas (machine d'authentification,
swicths, bornes...)"""
"""Form used to create and edit NAS devices."""
class Meta:
model = Nas
@ -514,7 +516,7 @@ class NasForm(FormRevMixin, ModelForm):
class DelNasForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs nas"""
"""Form used to delete one or several NAS devices."""
nas = forms.ModelMultipleChoiceField(
queryset=Nas.objects.none(),
@ -532,7 +534,7 @@ class DelNasForm(FormRevMixin, Form):
class RoleForm(FormRevMixin, ModelForm):
"""Add and edit role."""
"""Form used to add and edit roles."""
class Meta:
model = Role
@ -547,7 +549,7 @@ class RoleForm(FormRevMixin, ModelForm):
class DelRoleForm(FormRevMixin, Form):
"""Deletion of one or several roles."""
"""Form used to delete one or several roles."""
role = forms.ModelMultipleChoiceField(
queryset=Role.objects.none(),
@ -565,7 +567,7 @@ class DelRoleForm(FormRevMixin, Form):
class ServiceForm(FormRevMixin, ModelForm):
"""Ajout et edition d'une classe de service : dns, dhcp, etc"""
"""Form to add and edit services (DHCP, DNS etc.)."""
class Meta:
model = Service
@ -589,7 +591,7 @@ class ServiceForm(FormRevMixin, ModelForm):
class DelServiceForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs service"""
"""Form used to delete one or several services."""
service = forms.ModelMultipleChoiceField(
queryset=Service.objects.none(),
@ -607,7 +609,7 @@ class DelServiceForm(FormRevMixin, Form):
class VlanForm(FormRevMixin, ModelForm):
"""Ajout d'un vlan : id, nom"""
"""Form used to add and edit VLANs."""
class Meta:
model = Vlan
@ -619,7 +621,7 @@ class VlanForm(FormRevMixin, ModelForm):
class EditOptionVlanForm(FormRevMixin, ModelForm):
"""Ajout d'un vlan : id, nom"""
"""Form used to edit the options of a VLAN."""
class Meta:
model = Vlan
@ -631,7 +633,7 @@ class EditOptionVlanForm(FormRevMixin, ModelForm):
class DelVlanForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs vlans"""
"""Form used to delete one or several VLANs."""
vlan = forms.ModelMultipleChoiceField(
queryset=Vlan.objects.none(),
@ -649,8 +651,7 @@ class DelVlanForm(FormRevMixin, Form):
class EditOuverturePortConfigForm(FormRevMixin, ModelForm):
"""Edition de la liste des profils d'ouverture de ports
pour l'interface"""
"""Form to edit the ports opening list of an interface."""
class Meta:
model = Interface
@ -664,8 +665,7 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm):
class EditOuverturePortListForm(FormRevMixin, ModelForm):
"""Edition de la liste des ports et profils d'ouverture
des ports"""
"""Form used to add and edit ports opening lists."""
class Meta:
model = OuverturePortList
@ -677,7 +677,7 @@ class EditOuverturePortListForm(FormRevMixin, ModelForm):
class SshFpForm(FormRevMixin, ModelForm):
"""Edits a SSHFP record."""
"""Form used to add and edit SSHFP records."""
class Meta:
model = SshFp

File diff suppressed because it is too large Load diff

View file

@ -211,10 +211,11 @@ def generate_ipv4_mbf_param(form_obj, is_type_tt):
@can_create(Machine)
@can_edit(User)
def new_machine(request, user, **_kwargs):
""" Fonction de creation d'une machine. Cree l'objet machine,
le sous objet interface et l'objet domain à partir de model forms.
Trop complexe, devrait être simplifié"""
"""View used to create machines.
Creates the object, the underlying interface and domain objects from model
forms. Too complex, should be simplified.
"""
machine = NewMachineForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(request.POST or None, user=request.user)
domain = DomainForm(request.POST or None, user=user, initial={'name': user.get_next_domain_name()})
@ -249,10 +250,10 @@ def new_machine(request, user, **_kwargs):
@login_required
@can_edit(Interface)
def edit_interface(request, interface_instance, **_kwargs):
""" Edition d'une interface. Distingue suivant les droits les valeurs
de interfaces et machines que l'user peut modifier infra permet de
modifier le propriétaire"""
"""View used to edit interfaces.
The values a user can change depends on their rights.
"""
machine_form = EditMachineForm(
request.POST or None, instance=interface_instance.machine, user=request.user
)
@ -295,7 +296,7 @@ def edit_interface(request, interface_instance, **_kwargs):
@login_required
@can_delete(Machine)
def del_machine(request, machine, **_kwargs):
""" Supprime une machine, interfaces en mode cascade"""
"""View used to delete machines, and the interfaces in cascade."""
if request.method == "POST":
machine.delete()
messages.success(request, _("The machine was deleted."))
@ -311,8 +312,9 @@ def del_machine(request, machine, **_kwargs):
@can_create(Interface)
@can_edit(Machine)
def new_interface(request, machine, **_kwargs):
""" Ajoute une interface et son domain associé à une machine existante"""
"""View used to create interfaces and the associated domains related to a
machine.
"""
interface_form = AddInterfaceForm(request.POST or None, user=request.user)
domain_form = DomainForm(request.POST or None, user=request.user, initial={'name': machine.user.get_next_domain_name()})
if interface_form.is_valid():
@ -344,7 +346,7 @@ def new_interface(request, machine, **_kwargs):
@login_required
@can_delete(Interface)
def del_interface(request, interface, **_kwargs):
""" Supprime une interface. Domain objet en mode cascade"""
"""View used to delete interfaces, and the domains in cascade."""
if request.method == "POST":
machine = interface.machine
interface.delete()
@ -363,7 +365,7 @@ def del_interface(request, interface, **_kwargs):
@can_create(Ipv6List)
@can_edit(Interface)
def new_ipv6list(request, interface, **_kwargs):
"""Nouvelle ipv6"""
"""View used to create IPv6 addresses lists."""
ipv6list_instance = Ipv6List(interface=interface)
ipv6 = Ipv6ListForm(
request.POST or None, instance=ipv6list_instance, user=request.user
@ -384,7 +386,7 @@ def new_ipv6list(request, interface, **_kwargs):
@login_required
@can_edit(Ipv6List)
def edit_ipv6list(request, ipv6list_instance, **_kwargs):
"""Edition d'une ipv6"""
"""View used to edit IPv6 addresses lists."""
ipv6 = Ipv6ListForm(
request.POST or None, instance=ipv6list_instance, user=request.user
)
@ -406,7 +408,7 @@ def edit_ipv6list(request, ipv6list_instance, **_kwargs):
@login_required
@can_delete(Ipv6List)
def del_ipv6list(request, ipv6list, **_kwargs):
""" Supprime une ipv6"""
"""View used to delete IPv6 addresses lists."""
if request.method == "POST":
interfaceid = ipv6list.interface.id
ipv6list.delete()
@ -423,7 +425,7 @@ def del_ipv6list(request, ipv6list, **_kwargs):
@can_create(SshFp)
@can_edit(Machine)
def new_sshfp(request, machine, **_kwargs):
"""Creates an SSHFP record associated with a machine"""
"""View used to create SSHFP records associated with machines."""
sshfp_instance = SshFp(machine=machine)
sshfp = SshFpForm(request.POST or None, instance=sshfp_instance)
if sshfp.is_valid():
@ -442,7 +444,7 @@ def new_sshfp(request, machine, **_kwargs):
@login_required
@can_edit(SshFp)
def edit_sshfp(request, sshfp_instance, **_kwargs):
"""Edits an SSHFP record"""
"""View used to edit SSHFP records."""
sshfp = SshFpForm(request.POST or None, instance=sshfp_instance)
if sshfp.is_valid():
if sshfp.changed_data:
@ -462,7 +464,7 @@ def edit_sshfp(request, sshfp_instance, **_kwargs):
@login_required
@can_delete(SshFp)
def del_sshfp(request, sshfp, **_kwargs):
"""Deletes an SSHFP record"""
"""View used to delete SSHFP records."""
if request.method == "POST":
machineid = sshfp.machine.id
sshfp.delete()
@ -478,9 +480,10 @@ def del_sshfp(request, sshfp, **_kwargs):
@login_required
@can_create(IpType)
def add_iptype(request):
""" Ajoute un range d'ip. Intelligence dans le models, fonction views
minimaliste"""
"""View used to create IP ranges.
The view function is simple, the intelligence is in the model.
"""
iptype = IpTypeForm(request.POST or None)
if iptype.is_valid():
iptype.save()
@ -496,9 +499,10 @@ def add_iptype(request):
@login_required
@can_edit(IpType)
def edit_iptype(request, iptype_instance, **_kwargs):
""" Edition d'un range. Ne permet pas de le redimensionner pour éviter
l'incohérence"""
"""View used to edit IP ranges.
Changing the size of the range is not possible to prevent inconsistency.
"""
iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance)
if iptype.is_valid():
if iptype.changed_data:
@ -515,7 +519,10 @@ def edit_iptype(request, iptype_instance, **_kwargs):
@login_required
@can_delete_set(IpType)
def del_iptype(request, instances):
""" Suppression d'un range ip. Supprime les objets ip associés"""
"""View used to delete IP ranges.
Deletes the related IP objects.
"""
iptype = DelIpTypeForm(request.POST or None, instances=instances)
if iptype.is_valid():
iptype_dels = iptype.cleaned_data["iptypes"]
@ -545,7 +552,7 @@ def del_iptype(request, instances):
@login_required
@can_create(MachineType)
def add_machinetype(request):
""" View used to add a Machinetype object """
"""View used to create machine types."""
machinetype = MachineTypeForm(request.POST or None)
if machinetype.is_valid():
machinetype.save()
@ -561,7 +568,7 @@ def add_machinetype(request):
@login_required
@can_edit(MachineType)
def edit_machinetype(request, machinetype_instance, **_kwargs):
""" View used to edit a MachineType object """
"""View used to edit machine types."""
machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance)
if machinetype.is_valid():
if machinetype.changed_data:
@ -578,7 +585,7 @@ def edit_machinetype(request, machinetype_instance, **_kwargs):
@login_required
@can_delete_set(MachineType)
def del_machinetype(request, instances):
""" View used to delete a MachineType object """
"""View used to delete machines types."""
machinetype = DelMachineTypeForm(request.POST or None, instances=instances)
if machinetype.is_valid():
machinetype_dels = machinetype.cleaned_data["machinetypes"]
@ -608,7 +615,7 @@ def del_machinetype(request, instances):
@login_required
@can_create(Extension)
def add_extension(request):
""" View used to add an Extension object """
"""View used to create extensions."""
extension = ExtensionForm(request.POST or None)
if extension.is_valid():
extension.save()
@ -624,7 +631,7 @@ def add_extension(request):
@login_required
@can_edit(Extension)
def edit_extension(request, extension_instance, **_kwargs):
""" View used to edit an Extension object """
"""View used to edit extensions."""
extension = ExtensionForm(request.POST or None, instance=extension_instance)
if extension.is_valid():
if extension.changed_data:
@ -641,7 +648,7 @@ def edit_extension(request, extension_instance, **_kwargs):
@login_required
@can_delete_set(Extension)
def del_extension(request, instances):
""" View used to delete an Extension object """
"""View used to delete extensions."""
extension = DelExtensionForm(request.POST or None, instances=instances)
if extension.is_valid():
extension_dels = extension.cleaned_data["extensions"]
@ -670,7 +677,7 @@ def del_extension(request, instances):
@login_required
@can_create(SOA)
def add_soa(request):
""" View used to add a SOA object """
"""View used to create SOA records."""
soa = SOAForm(request.POST or None)
if soa.is_valid():
soa.save()
@ -686,7 +693,7 @@ def add_soa(request):
@login_required
@can_edit(SOA)
def edit_soa(request, soa_instance, **_kwargs):
""" View used to edit a SOA object """
"""View used to edit SOA records."""
soa = SOAForm(request.POST or None, instance=soa_instance)
if soa.is_valid():
if soa.changed_data:
@ -701,7 +708,7 @@ def edit_soa(request, soa_instance, **_kwargs):
@login_required
@can_delete_set(SOA)
def del_soa(request, instances):
""" View used to delete a SOA object """
"""View used to delete SOA records."""
soa = DelSOAForm(request.POST or None, instances=instances)
if soa.is_valid():
soa_dels = soa.cleaned_data["soa"]
@ -722,7 +729,7 @@ def del_soa(request, instances):
@login_required
@can_create(Mx)
def add_mx(request):
""" View used to add a MX object """
"""View used to create MX records."""
mx = MxForm(request.POST or None)
if mx.is_valid():
mx.save()
@ -738,7 +745,7 @@ def add_mx(request):
@login_required
@can_edit(Mx)
def edit_mx(request, mx_instance, **_kwargs):
""" View used to edit a MX object """
"""View used to edit MX records."""
mx = MxForm(request.POST or None, instance=mx_instance)
if mx.is_valid():
if mx.changed_data:
@ -753,7 +760,7 @@ def edit_mx(request, mx_instance, **_kwargs):
@login_required
@can_delete_set(Mx)
def del_mx(request, instances):
""" View used to delete a MX object """
"""View used to delete MX records."""
mx = DelMxForm(request.POST or None, instances=instances)
if mx.is_valid():
mx_dels = mx.cleaned_data["mx"]
@ -774,7 +781,7 @@ def del_mx(request, instances):
@login_required
@can_create(Ns)
def add_ns(request):
""" View used to add a NS object """
"""View used to create NS records."""
ns = NsForm(request.POST or None)
if ns.is_valid():
ns.save()
@ -790,7 +797,7 @@ def add_ns(request):
@login_required
@can_edit(Ns)
def edit_ns(request, ns_instance, **_kwargs):
""" View used to edit a NS object """
"""View used to edit NS records."""
ns = NsForm(request.POST or None, instance=ns_instance)
if ns.is_valid():
if ns.changed_data:
@ -805,7 +812,7 @@ def edit_ns(request, ns_instance, **_kwargs):
@login_required
@can_delete_set(Ns)
def del_ns(request, instances):
""" View used to delete a NS object """
"""View used to delete NS records."""
nss = DelNsForm(request.POST or None, instances=instances)
if nss.is_valid():
ns_dels = nss.cleaned_data["nss"]
@ -826,7 +833,7 @@ def del_ns(request, instances):
@login_required
@can_create(DName)
def add_dname(request):
""" View used to add a DName object """
"""View used to create DNAME records."""
dname = DNameForm(request.POST or None)
if dname.is_valid():
dname.save()
@ -842,7 +849,7 @@ def add_dname(request):
@login_required
@can_edit(DName)
def edit_dname(request, dname_instance, **_kwargs):
""" View used to edit a DName object """
"""View used to edit DNAME records."""
dname = DNameForm(request.POST or None, instance=dname_instance)
if dname.is_valid():
if dname.changed_data:
@ -857,7 +864,7 @@ def edit_dname(request, dname_instance, **_kwargs):
@login_required
@can_delete_set(DName)
def del_dname(request, instances):
""" View used to delete a DName object """
"""View used to delete DNAME records."""
dname = DelDNameForm(request.POST or None, instances=instances)
if dname.is_valid():
dname_dels = dname.cleaned_data["dname"]
@ -881,7 +888,7 @@ def del_dname(request, instances):
@login_required
@can_create(Txt)
def add_txt(request):
""" View used to add a TXT object """
"""View used to create TXT records."""
txt = TxtForm(request.POST or None)
if txt.is_valid():
txt.save()
@ -897,7 +904,7 @@ def add_txt(request):
@login_required
@can_edit(Txt)
def edit_txt(request, txt_instance, **_kwargs):
""" View used to edit a TXT object """
"""View used to edit TXT records."""
txt = TxtForm(request.POST or None, instance=txt_instance)
if txt.is_valid():
if txt.changed_data:
@ -912,7 +919,7 @@ def edit_txt(request, txt_instance, **_kwargs):
@login_required
@can_delete_set(Txt)
def del_txt(request, instances):
""" View used to delete a TXT object """
"""View used to delete TXT records."""
txt = DelTxtForm(request.POST or None, instances=instances)
if txt.is_valid():
txt_dels = txt.cleaned_data["txt"]
@ -933,7 +940,7 @@ def del_txt(request, instances):
@login_required
@can_create(Srv)
def add_srv(request):
""" View used to add a SRV object """
"""View used to create SRV records."""
srv = SrvForm(request.POST or None)
if srv.is_valid():
srv.save()
@ -949,7 +956,7 @@ def add_srv(request):
@login_required
@can_edit(Srv)
def edit_srv(request, srv_instance, **_kwargs):
""" View used to edit a SRV object """
"""View used to edit SRV records."""
srv = SrvForm(request.POST or None, instance=srv_instance)
if srv.is_valid():
if srv.changed_data:
@ -964,7 +971,7 @@ def edit_srv(request, srv_instance, **_kwargs):
@login_required
@can_delete_set(Srv)
def del_srv(request, instances):
""" View used to delete a SRV object """
"""View used to delete SRV records."""
srv = DelSrvForm(request.POST or None, instances=instances)
if srv.is_valid():
srv_dels = srv.cleaned_data["srv"]
@ -986,7 +993,7 @@ def del_srv(request, instances):
@can_create(Domain)
@can_edit(Interface)
def add_alias(request, interface, interfaceid):
""" View used to add an Alias object """
"""View used to create aliases."""
alias = AliasForm(request.POST or None, user=request.user)
if alias.is_valid():
alias = alias.save(commit=False)
@ -1006,7 +1013,7 @@ def add_alias(request, interface, interfaceid):
@login_required
@can_edit(Domain)
def edit_alias(request, domain_instance, **_kwargs):
""" View used to edit an Alias object """
"""View used to edit aliases records."""
alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user)
if alias.is_valid():
if alias.changed_data:
@ -1026,7 +1033,7 @@ def edit_alias(request, domain_instance, **_kwargs):
@login_required
@can_edit(Interface)
def del_alias(request, interface, interfaceid):
""" View used to delete an Alias object """
"""View used to delete aliases records."""
alias = DelAliasForm(request.POST or None, interface=interface)
if alias.is_valid():
alias_dels = alias.cleaned_data["alias"]
@ -1051,7 +1058,7 @@ def del_alias(request, interface, interfaceid):
@login_required
@can_create(Role)
def add_role(request):
""" View used to add a Role object """
"""View used to create roles."""
role = RoleForm(request.POST or None)
if role.is_valid():
role.save()
@ -1067,7 +1074,7 @@ def add_role(request):
@login_required
@can_edit(Role)
def edit_role(request, role_instance, **_kwargs):
""" View used to edit a Role object """
"""View used to edit roles."""
role = RoleForm(request.POST or None, instance=role_instance)
if role.is_valid():
if role.changed_data:
@ -1082,7 +1089,7 @@ def edit_role(request, role_instance, **_kwargs):
@login_required
@can_delete_set(Role)
def del_role(request, instances):
""" View used to delete a Service object """
"""View used to delete roles."""
role = DelRoleForm(request.POST or None, instances=instances)
if role.is_valid():
role_dels = role.cleaned_data["role"]
@ -1103,7 +1110,7 @@ def del_role(request, instances):
@login_required
@can_create(Service)
def add_service(request):
""" View used to add a Service object """
"""View used to create services."""
service = ServiceForm(request.POST or None)
if service.is_valid():
service.save()
@ -1119,7 +1126,7 @@ def add_service(request):
@login_required
@can_edit(Service)
def edit_service(request, service_instance, **_kwargs):
""" View used to edit a Service object """
"""View used to edit services."""
service = ServiceForm(request.POST or None, instance=service_instance)
if service.is_valid():
if service.changed_data:
@ -1136,7 +1143,7 @@ def edit_service(request, service_instance, **_kwargs):
@login_required
@can_delete_set(Service)
def del_service(request, instances):
""" View used to delete a Service object """
"""View used to delete services."""
service = DelServiceForm(request.POST or None, instances=instances)
if service.is_valid():
service_dels = service.cleaned_data["service"]
@ -1169,7 +1176,7 @@ def regen_service(request, service, **_kwargs):
@login_required
@can_create(Vlan)
def add_vlan(request):
""" View used to add a VLAN object """
"""View used to create VLANs."""
vlan = VlanForm(request.POST or None)
if vlan.is_valid():
vlan.save()
@ -1185,7 +1192,7 @@ def add_vlan(request):
@login_required
@can_edit(Vlan)
def edit_vlan(request, vlan_instance, **_kwargs):
""" View used to edit a VLAN object """
"""View used to edit VLANs."""
vlan = VlanForm(request.POST or None, instance=vlan_instance)
if vlan.is_valid():
if vlan.changed_data:
@ -1200,7 +1207,7 @@ def edit_vlan(request, vlan_instance, **_kwargs):
@login_required
@can_delete_set(Vlan)
def del_vlan(request, instances):
""" View used to delete a VLAN object """
"""View used to delete VLANs."""
vlan = DelVlanForm(request.POST or None, instances=instances)
if vlan.is_valid():
vlan_dels = vlan.cleaned_data["vlan"]
@ -1221,7 +1228,7 @@ def del_vlan(request, instances):
@login_required
@can_create(Nas)
def add_nas(request):
""" View used to add a NAS object """
"""View used to create NAS devices."""
nas = NasForm(request.POST or None)
if nas.is_valid():
nas.save()
@ -1237,7 +1244,7 @@ def add_nas(request):
@login_required
@can_edit(Nas)
def edit_nas(request, nas_instance, **_kwargs):
""" View used to edit a NAS object """
"""View used to edit NAS devices."""
nas = NasForm(request.POST or None, instance=nas_instance)
if nas.is_valid():
if nas.changed_data:
@ -1252,7 +1259,7 @@ def edit_nas(request, nas_instance, **_kwargs):
@login_required
@can_delete_set(Nas)
def del_nas(request, instances):
""" View used to delete a NAS object """
"""View used to delete NAS devices."""
nas = DelNasForm(request.POST or None, instances=instances)
if nas.is_valid():
nas_dels = nas.cleaned_data["nas"]
@ -1273,8 +1280,8 @@ def del_nas(request, instances):
@login_required
@can_view_all(Machine)
def index(request):
""" The home view for this app. Displays the list of registered
machines in Re2o """
"""The home view for this app. Displays the list of registered
machines in Re2o."""
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
machines_list = (
Machine.objects.select_related("user")
@ -1297,7 +1304,7 @@ def index(request):
@login_required
@can_view_all(IpType)
def index_iptype(request):
""" View displaying the list of existing types of IP """
"""View used to display the list of existing types of IP."""
iptype_list = (
IpType.objects.select_related("extension")
.select_related("vlan")
@ -1309,7 +1316,7 @@ def index_iptype(request):
@login_required
@can_view_all(Vlan)
def index_vlan(request):
""" View displaying the list of existing VLANs """
"""View used to display the list of existing VLANs."""
vlan_list = Vlan.objects.prefetch_related("iptype_set").order_by("vlan_id")
return render(request, "machines/index_vlan.html", {"vlan_list": vlan_list})
@ -1317,7 +1324,7 @@ def index_vlan(request):
@login_required
@can_view_all(MachineType)
def index_machinetype(request):
""" View displaying the list of existing types of machines """
"""View used to display the list of existing types of machines."""
machinetype_list = MachineType.objects.select_related("ip_type").order_by("name")
return render(
request,
@ -1329,7 +1336,7 @@ def index_machinetype(request):
@login_required
@can_view_all(Nas)
def index_nas(request):
""" View displaying the list of existing NAS """
"""View used to display the list of existing NAS devices."""
nas_list = (
Nas.objects.select_related("machine_type")
.select_related("nas_type")
@ -1341,10 +1348,11 @@ def index_nas(request):
@login_required
@can_view_all(SOA, Mx, Ns, Txt, DName, Srv, Extension)
def index_extension(request):
""" View displaying the list of existing extensions, the list of
"""View used to display the list of existing extensions, the list of
existing SOA records, the list of existing MX records , the list of
existing NS records, the list of existing TXT records and the list of
existing SRV records """
existing SRV records.
"""
extension_list = (
Extension.objects.select_related("origin")
.select_related("soa")
@ -1386,7 +1394,7 @@ def index_extension(request):
@login_required
@can_edit(Interface)
def index_alias(request, interface, interfaceid):
""" View used to display the list of existing alias of an interface """
"""View used to display the list of existing aliases of an interface."""
alias_list = Domain.objects.filter(
cname=Domain.objects.filter(interface_parent=interface)
).order_by("name")
@ -1401,7 +1409,7 @@ def index_alias(request, interface, interfaceid):
@can_view(Machine)
def index_sshfp(request, machine, machineid):
"""View used to display the list of existing SSHFP records associated
with a machine"""
with a machine."""
sshfp_list = SshFp.objects.filter(machine=machine)
return render(
request,
@ -1413,7 +1421,7 @@ def index_sshfp(request, machine, machineid):
@login_required
@can_view(Interface)
def index_ipv6(request, interface, interfaceid):
""" View used to display the list of existing IPv6 of an interface """
"""View used to display the list of existing IPv6 of an interface."""
ipv6_list = Ipv6List.objects.filter(interface=interface)
return render(
request,
@ -1425,7 +1433,7 @@ def index_ipv6(request, interface, interfaceid):
@login_required
@can_view_all(Role)
def index_role(request):
""" View used to display the list of existing roles """
"""View used to display the list of existing roles."""
role_list = Role.objects.prefetch_related("servers__domain__extension").all()
return render(request, "machines/index_role.html", {"role_list": role_list})
@ -1433,7 +1441,7 @@ def index_role(request):
@login_required
@can_view_all(Service)
def index_service(request):
""" View used to display the list of existing services """
"""View used to display the list of existing services."""
service_list = Service.objects.prefetch_related(
"service_link_set__server__domain__extension"
).all()
@ -1452,7 +1460,7 @@ def index_service(request):
@login_required
@can_view_all(OuverturePortList)
def index_portlist(request):
""" View used to display the list of existing port policies """
"""View used to display the list of existing port policies."""
port_list = (
OuverturePortList.objects.prefetch_related("ouvertureport_set")
.prefetch_related("interface_set__domain__extension")
@ -1465,7 +1473,7 @@ def index_portlist(request):
@login_required
@can_edit(OuverturePortList)
def edit_portlist(request, ouvertureportlist_instance, **_kwargs):
""" View used to edit a port policy """
"""View used to edit port policies."""
port_list = EditOuverturePortListForm(
request.POST or None, instance=ouvertureportlist_instance
)
@ -1500,7 +1508,7 @@ def edit_portlist(request, ouvertureportlist_instance, **_kwargs):
@login_required
@can_delete(OuverturePortList)
def del_portlist(request, port_list_instance, **_kwargs):
""" View used to delete a port policy """
"""View used to delete port policies."""
port_list_instance.delete()
messages.success(request, _("The ports list was deleted."))
return redirect(reverse("machines:index-portlist"))
@ -1509,7 +1517,7 @@ def del_portlist(request, port_list_instance, **_kwargs):
@login_required
@can_create(OuverturePortList)
def add_portlist(request):
""" View used to add a port policy """
"""View used to create port policies."""
port_list = EditOuverturePortListForm(request.POST or None)
port_formset = modelformset_factory(
OuverturePort,
@ -1540,8 +1548,9 @@ def add_portlist(request):
@can_create(OuverturePort)
@can_edit(Interface)
def configure_ports(request, interface_instance, **_kwargs):
""" View to display the list of configured port policy for an
interface """
"""View to display the list of configured port policies for an
interface.
"""
if not interface_instance.may_have_port_open():
messages.error(
request,

View file

@ -36,7 +36,7 @@ from topologie.models import Dormitory
class DormitoryForm(FormRevMixin, Form):
"""Select a dorm"""
"""Form used to select dormitories."""
dormitory = forms.ModelMultipleChoiceField(
queryset=Dormitory.objects.all(),

View file

@ -34,7 +34,7 @@ from .models import Preferences
class EditPreferencesForm(ModelForm):
""" Edit the ticket's settings"""
"""Form used to edit the settings of multi_op."""
class Meta:
model = Preferences

View file

@ -19,7 +19,8 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Fichier définissant les administration des models de preference
multi_op preferences model. The settings are used when managing dormitories
with multiple operators.
"""
@ -28,7 +29,7 @@ from django.utils.translation import ugettext_lazy as _
class Preferences(models.Model):
""" Definition of the app settings"""
"""Definition of the settings of multi_op."""
enabled_dorm = models.ManyToManyField(
"topologie.Dormitory",

View file

@ -53,7 +53,13 @@ from .preferences.forms import EditPreferencesForm
def display_rooms_connection(request, dormitory=None):
"""View to display global state of connection state"""
"""View used to display an overview of the rooms' connection state.
Args:
request: django request.
dormitory: Dormitory, the dormitory used to filter rooms. If no
dormitory is given, all rooms are displayed (default: None).
"""
room_list = Room.objects.select_related("building__dormitory").order_by(
"building_dormitory", "port"
)
@ -81,19 +87,30 @@ def display_rooms_connection(request, dormitory=None):
@login_required
@can_view_all(Room)
def aff_state_global(request):
"""View used to display the connection state of all rooms."""
return display_rooms_connection(request)
@login_required
@can_view(Dormitory)
def aff_state_dormitory(request, dormitory, dormitoryid):
"""View used to display the connection state of the rooms in the given
dormitory.
Args:
request: django request.
dormitory: Dormitory, the dormitory used to filter rooms.
dormitoryid: int, the id of the dormitory.
"""
return display_rooms_connection(dormitory=dormitory)
@login_required
@can_view_all(Room)
def aff_pending_connection(request):
"""Aff pending Rooms to connect on our network"""
"""View used to display rooms pending connection to the organisation's
network.
"""
room_list = (
Room.objects.select_related("building__dormitory")
.filter(port__isnull=True)
@ -128,7 +145,9 @@ def aff_pending_connection(request):
@login_required
@can_view_all(Room)
def aff_pending_disconnection(request):
"""Aff pending Rooms to disconnect from our network"""
"""View used to display rooms pending disconnection from the organisation's
network.
"""
room_list = (
Room.objects.select_related("building__dormitory")
.filter(port__isnull=False)
@ -163,7 +182,13 @@ def aff_pending_disconnection(request):
@login_required
@can_edit(Room)
def disconnect_room(request, room, roomid):
"""Action of disconnecting a room"""
"""View used to disconnect a room.
Args:
request: django request.
room: Room, the room to be disconnected.
roomid: int, the id of the room.
"""
room.port_set.clear()
room.save()
messages.success(request, _("The room %s was disconnected.") % room)
@ -171,5 +196,7 @@ def disconnect_room(request, room, roomid):
def navbar_user():
"""View to display the app in user's dropdown in the navbar"""
"""View used to display a link to manage operators in the navbar (in the
dropdown menu Topology).
"""
return ("topologie", render_to_string("multi_op/navbar.html"))

View file

@ -21,7 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Classes admin pour les models de preferences
Admin classes for models of preferences app.
"""
from __future__ import unicode_literals
@ -46,79 +46,79 @@ from .models import (
class OptionalUserAdmin(VersionAdmin):
"""Class admin options user"""
"""Admin class for user options."""
pass
class OptionalTopologieAdmin(VersionAdmin):
"""Class admin options topologie"""
"""Admin class for topology options."""
pass
class OptionalMachineAdmin(VersionAdmin):
"""Class admin options machines"""
"""Admin class for machines options."""
pass
class GeneralOptionAdmin(VersionAdmin):
"""Class admin options générales"""
"""Admin class for general options."""
pass
class ServiceAdmin(VersionAdmin):
"""Class admin gestion des services de la page d'accueil"""
"""Admin class for services (on the homepage)."""
pass
class MailContactAdmin(VersionAdmin):
"""Admin class for contact email adresses"""
"""Admin class for contact email addresses."""
pass
class AssoOptionAdmin(VersionAdmin):
"""Class admin options de l'asso"""
"""Admin class for organisation options."""
pass
class MailMessageOptionAdmin(VersionAdmin):
"""Class admin options mail"""
"""Admin class for email messages options."""
pass
class HomeOptionAdmin(VersionAdmin):
"""Class admin options home"""
"""Admin class for home options."""
pass
class RadiusKeyAdmin(VersionAdmin):
"""Class radiuskey"""
"""Admin class for RADIUS keys options."""
pass
class SwitchManagementCredAdmin(VersionAdmin):
"""Class managementcred for switch"""
"""Admin class for switch management credentials options."""
pass
class ReminderAdmin(VersionAdmin):
"""Class reminder for switch"""
"""Admin class for reminder options."""
pass
class DocumentTemplateAdmin(VersionAdmin):
"""Admin class for DocumentTemplate"""
"""Admin class for document templates."""
pass

View file

@ -20,7 +20,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Formulaire d'edition des réglages : user, machine, topologie, asso...
Forms to edit preferences: users, machines, topology, organisation etc.
"""
from __future__ import unicode_literals
@ -53,7 +53,7 @@ from topologie.models import Switch
class EditOptionalUserForm(ModelForm):
"""Formulaire d'édition des options de l'user. (solde, telephone..)"""
"""Form used to edit user preferences."""
class Meta:
model = OptionalUser
@ -82,7 +82,7 @@ class EditOptionalUserForm(ModelForm):
class EditOptionalMachineForm(ModelForm):
"""Options machines (max de machines, etc)"""
"""Form used to edit machine preferences."""
class Meta:
model = OptionalMachine
@ -105,9 +105,7 @@ class EditOptionalMachineForm(ModelForm):
class EditOptionalTopologieForm(ModelForm):
"""Options de topologie, formulaire d'edition (vlan par default etc)
On rajoute un champ automatic provision switchs pour gérer facilement
l'ajout de switchs au provisionning automatique"""
"""Form used to edit the configuration of switches."""
automatic_provision_switchs = forms.ModelMultipleChoiceField(
Switch.objects.all(), required=False
@ -135,7 +133,7 @@ class EditOptionalTopologieForm(ModelForm):
class EditGeneralOptionForm(ModelForm):
"""Options générales (affichages de résultats de recherche, etc)"""
"""Form used to edit general preferences."""
class Meta:
model = GeneralOption
@ -165,7 +163,7 @@ class EditGeneralOptionForm(ModelForm):
class EditAssoOptionForm(ModelForm):
"""Options de l'asso (addresse, telephone, etc)"""
"""Form used to edit information about the organisation."""
class Meta:
model = AssoOption
@ -189,7 +187,7 @@ class EditAssoOptionForm(ModelForm):
class EditMailMessageOptionForm(ModelForm):
"""Formulaire d'edition des messages de bienvenue personnalisés"""
"""Form used to edit welcome email messages."""
class Meta:
model = MailMessageOption
@ -207,7 +205,9 @@ class EditMailMessageOptionForm(ModelForm):
class EditHomeOptionForm(ModelForm):
"""Edition forms of Home options"""
"""Form used to edit the social networks information displayed on the home
page.
"""
class Meta:
model = HomeOption
@ -222,7 +222,7 @@ class EditHomeOptionForm(ModelForm):
class EditRadiusOptionForm(ModelForm):
"""Edition forms for Radius options"""
"""Form used to edit RADIUS preferences."""
class Meta:
model = RadiusOption
@ -242,7 +242,7 @@ class EditRadiusOptionForm(ModelForm):
class EditCotisationsOptionForm(ModelForm):
"""Edition forms for Cotisations options"""
"""Form used to edit subscription preferences."""
class Meta:
model = CotisationsOption
@ -250,7 +250,7 @@ class EditCotisationsOptionForm(ModelForm):
class MandateForm(ModelForm):
"""Edit Mandates"""
"""Form used to add and edit mandates."""
class Meta:
model = Mandate
@ -319,7 +319,7 @@ class MandateForm(ModelForm):
class ServiceForm(ModelForm):
"""Edition, ajout de services sur la page d'accueil"""
"""Form used to add and edit services displayed on the home page."""
class Meta:
model = Service
@ -335,7 +335,8 @@ class ServiceForm(ModelForm):
class DelServiceForm(Form):
"""Suppression de services sur la page d'accueil"""
"""Form used to delete one or several services displayed on the home page.
"""
services = forms.ModelMultipleChoiceField(
queryset=Service.objects.none(),
@ -353,7 +354,7 @@ class DelServiceForm(Form):
class ReminderForm(FormRevMixin, ModelForm):
"""Edition, ajout de services sur la page d'accueil"""
"""Form used to add and edit reminders."""
class Meta:
model = Reminder
@ -365,7 +366,7 @@ class ReminderForm(FormRevMixin, ModelForm):
class RadiusKeyForm(FormRevMixin, ModelForm):
"""Edition, ajout de clef radius"""
"""Form used to add and edit RADIUS keys."""
members = forms.ModelMultipleChoiceField(
queryset=Switch.objects.all(), required=False
@ -389,8 +390,7 @@ class RadiusKeyForm(FormRevMixin, ModelForm):
class SwitchManagementCredForm(FormRevMixin, ModelForm):
"""Edition, ajout de creds de management pour gestion
et interface rest des switchs"""
"""Form used to add and edit switch management credentials."""
members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False)
@ -412,7 +412,7 @@ class SwitchManagementCredForm(FormRevMixin, ModelForm):
class MailContactForm(ModelForm):
"""Edition, ajout d'adresse de contact"""
"""Form used to add and edit contact email addresses."""
class Meta:
model = MailContact
@ -424,7 +424,7 @@ class MailContactForm(ModelForm):
class DelMailContactForm(Form):
"""Delete contact email adress"""
"""Form used to delete one or several contact email addresses."""
mailcontacts = forms.ModelMultipleChoiceField(
queryset=MailContact.objects.none(),
@ -442,9 +442,7 @@ class DelMailContactForm(Form):
class DocumentTemplateForm(FormRevMixin, ModelForm):
"""
Form used to create a document template.
"""
"""Form used to add and edit document templates."""
class Meta:
model = DocumentTemplate
@ -456,10 +454,7 @@ class DocumentTemplateForm(FormRevMixin, ModelForm):
class DelDocumentTemplateForm(FormRevMixin, Form):
"""
Form used to delete one or more document templatess.
The use must choose the one to delete by checking the boxes.
"""
"""Form used to delete one or several document templates."""
document_templates = forms.ModelMultipleChoiceField(
queryset=DocumentTemplate.objects.none(),
@ -477,7 +472,7 @@ class DelDocumentTemplateForm(FormRevMixin, Form):
class RadiusAttributeForm(ModelForm):
"""Edit and add RADIUS attributes."""
"""Form used to add and edit RADIUS attributes."""
class Meta:
model = RadiusAttribute
@ -489,7 +484,7 @@ class RadiusAttributeForm(ModelForm):
class DelRadiusAttributeForm(Form):
"""Delete RADIUS attributes"""
"""Form used to delete one or several RADIUS attributes."""
attributes = forms.ModelMultipleChoiceField(
queryset=RadiusAttribute.objects.none(),

View file

@ -21,7 +21,8 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Reglages généraux, machines, utilisateurs, mail, general pour l'application.
Models defining the preferences for users, machines, emails, general settings
etc.
"""
from __future__ import unicode_literals
import os
@ -44,20 +45,22 @@ 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) """
"""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 """
"""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 """
"""Get the preferences from the server-side cache."""
instance = cache.get(cls().__class__.__name__.lower())
if instance is None:
instance = cls.set_in_cache()
@ -68,8 +71,34 @@ class PreferencesModel(models.Model):
class OptionalUser(AclMixin, PreferencesModel):
"""Options pour l'user : obligation ou nom du telephone,
activation ou non du solde, autorisation du negatif, fingerprint etc"""
"""User preferences: telephone number requirement, user balance activation,
creation of users by everyone etc.
Attributes:
is_tel_mandatory: whether indicating a telephone number is mandatory.
gpg_fingerprint: whether GPG fingerprints are enabled.
all_can_create_club: whether all users can create a club.
all_can_create_adherent: whether all users can create a member.
shell_default: the default shell for users connecting to machines
managed by the organisation.
self_change_shell: whether users can edit their shell.
self_change_pseudo: whether users can edit their pseudo (username).
self_room_policy: whether users can edit the policy of their room.
local_email_accounts_enabled: whether local email accounts are enabled.
local_email_domain: the domain used for local email accounts.
max_email_address: the maximum number of local email addresses allowed
for a standard user.
delete_notyetactive: the number of days before deleting not yet active
users.
disable_emailnotyetconfirmed: the number of days before disabling users
with not yet verified email address.
self_adhesion: whether users can create their account themselves.
all_users_active: whether newly created users are active.
allow_set_password_during_user_creation: whether users can set their
password directly when creating their account.
allow_archived_connexion: whether archived users can connect on the web
interface.
"""
DISABLED = "DISABLED"
ONLY_INACTIVE = "ONLY_INACTIVE"
@ -158,23 +187,32 @@ class OptionalUser(AclMixin, PreferencesModel):
verbose_name = _("user preferences")
def clean(self):
"""Clean model:
Check the mail_extension
"""
"""Check the email extension."""
if self.local_email_domain[0] != "@":
raise ValidationError(_("Email domain must begin with @."))
@receiver(post_save, sender=OptionalUser)
def optionaluser_post_save(**kwargs):
"""Ecriture dans le cache"""
"""Write in the cache."""
user_pref = kwargs["instance"]
user_pref.set_in_cache()
class OptionalMachine(AclMixin, PreferencesModel):
"""Options pour les machines : maximum de machines ou d'alias par user
sans droit, activation de l'ipv6"""
"""Machines preferences: maximum number of machines per user, IPv6
activation etc.
Attributes:
password_machine: whether password per machine is enabled.
max_lambdauser_interfaces: the maximum number of interfaces allowed for
a standard user.
max_lambdauser_aliases: the maximum number of aliases allowed for a
standard user.
ipv6_mode: whether IPv6 mode is enabled.
create_machine: whether creation of machine is enabled.
default_dns_ttl: the default TTL for CNAME, A and AAAA records.
"""
SLAAC = "SLAAC"
DHCPV6 = "DHCPV6"
@ -197,7 +235,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@cached_property
def ipv6(self):
""" Check if the IPv6 option is activated """
"""Check if the IPv6 mode is enabled."""
return not self.get_cached_value("ipv6_mode") == "DISABLED"
class Meta:
@ -207,7 +245,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalMachine)
def optionalmachine_post_save(**kwargs):
"""Synchronisation ipv6 et ecriture dans le cache"""
"""Synchronise IPv6 mode and write in the cache."""
machine_pref = kwargs["instance"]
machine_pref.set_in_cache()
if machine_pref.ipv6_mode != "DISABLED":
@ -216,8 +254,21 @@ def optionalmachine_post_save(**kwargs):
class OptionalTopologie(AclMixin, PreferencesModel):
"""Reglages pour la topologie : mode d'accès radius, vlan où placer
les machines en accept ou reject"""
"""Configuration of switches: automatic provision, RADIUS mode, default
VLANs etc.
Attributes:
switchs_web_management: whether web management for automatic provision
is enabled.
switchs_web_management_ssl: whether SSL web management is required.
switchs_rest_management: whether REST management for automatic
provision is enabled.
switchs_ip_type: the IP range for the management of switches.
switchs_provision: the provision mode for switches to get their
configuration.
sftp_login: the SFTP login for switches.
sftp_pass: the SFTP password for switches.
"""
MACHINE = "MACHINE"
DEFINED = "DEFINED"
@ -264,7 +315,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property
def provisioned_switchs(self):
"""Liste des switches provisionnés"""
"""Get the list of provisioned switches."""
from topologie.models import Switch
return Switch.objects.filter(automatic_provision=True).order_by(
@ -273,7 +324,9 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property
def switchs_management_interface(self):
"""Return the ip of the interface that the switch have to contact to get it's config"""
"""Get the interface that the switch has to contact to get its
configuration.
"""
if self.switchs_ip_type:
from machines.models import Role, Interface
@ -291,14 +344,16 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property
def switchs_management_interface_ip(self):
"""Same, but return the ipv4"""
"""Get the IPv4 address of the interface that the switch has to contact
to get its configuration.
"""
if not self.switchs_management_interface:
return None
return self.switchs_management_interface.ipv4
@cached_property
def switchs_management_sftp_creds(self):
"""Credentials des switchs pour provion sftp"""
"""Get the switch credentials for SFTP provisioning."""
if self.sftp_login and self.sftp_pass:
return {"login": self.sftp_login, "pass": self.sftp_pass}
else:
@ -306,7 +361,9 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property
def switchs_management_utils(self):
"""Used for switch_conf, return a list of ip on vlans"""
"""Get the dictionary of IP addresses for the configuration of
switches.
"""
from machines.models import Role, Ipv6List, Interface
def return_ips_dict(interfaces):
@ -350,8 +407,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property
def provision_switchs_enabled(self):
"""Return true if all settings are ok : switchs on automatic provision,
ip_type"""
"""Check if all automatic provisioning settings are OK."""
return bool(
self.provisioned_switchs
and self.switchs_ip_type
@ -371,13 +427,20 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalTopologie)
def optionaltopologie_post_save(**kwargs):
"""Ecriture dans le cache"""
"""Write in the cache."""
topologie_pref = kwargs["instance"]
topologie_pref.set_in_cache()
class RadiusKey(AclMixin, models.Model):
"""Class of a radius key"""
"""Class of a RADIUS key.
Attributes:
radius_key: the encrypted RADIUS key.
comment: a comment related to the key.
default_switch: bool, True if the key is to be used by default on
switches and False otherwise.
"""
radius_key = AESEncryptedField(max_length=255, help_text=_("RADIUS key."))
comment = models.CharField(
@ -393,9 +456,7 @@ class RadiusKey(AclMixin, models.Model):
verbose_name_plural = _("RADIUS keys")
def clean(self):
"""Clean model:
Check default switch is unique
"""
"""Check if there is a unique default RADIUS key."""
if RadiusKey.objects.filter(default_switch=True).count() > 1:
raise ValidationError(_("Default RADIUS key for switches already exists."))
@ -404,7 +465,14 @@ class RadiusKey(AclMixin, models.Model):
class SwitchManagementCred(AclMixin, models.Model):
"""Class of a management creds of a switch, for rest management"""
"""Class of a switch management credentials, for rest management.
Attributes:
management_id: the login used to connect to switches.
management_pass: the encrypted password used to connect to switches.
default_switch: bool, True if the credentials are to be used by default
on switches and False otherwise.
"""
management_id = models.CharField(max_length=63, help_text=_("Switch login."))
management_pass = AESEncryptedField(max_length=63, help_text=_("Password."))
@ -426,9 +494,13 @@ class SwitchManagementCred(AclMixin, models.Model):
class Reminder(AclMixin, models.Model):
"""Options pour les mails de notification de fin d'adhésion.
Days: liste des nombres de jours pour lesquells un mail est envoyé
optionalMessage: message additionel pour le mail
"""Reminder of membership's end preferences: email messages, number of days
before sending emails.
Attributes:
days: the number of days before the membership's end to send the
reminder.
message: the content of the reminder.
"""
days = models.IntegerField(
@ -460,8 +532,26 @@ class Reminder(AclMixin, models.Model):
class GeneralOption(AclMixin, PreferencesModel):
"""Options générales : nombre de resultats par page, nom du site,
temps les liens sont valides"""
"""General preferences: number of search results per page, website name
etc.
Attributes:
general_message_fr: general message displayed on the French version of
the website (e.g. in case of maintenance).
general_message_en: general message displayed on the English version of
the website (e.g. in case of maintenance).
search_display_page: number of results displayed (in each category)
when searching.
pagination_number: number of items per page (standard size).
pagination_large_number: number of items per page (large size).
req_expire_hrs: number of hours before expiration of the reset password
link.
site_name: website name.
email_from: email address for automatic emailing.
main_site_url: main site URL.
GTU_sum_up: summary of the General Terms of Use.
GTU: file, General Terms of Use.
"""
general_message_fr = models.TextField(
default="",
@ -496,14 +586,20 @@ class GeneralOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=GeneralOption)
def generaloption_post_save(**kwargs):
"""Ecriture dans le cache"""
"""Write in the cache."""
general_pref = kwargs["instance"]
general_pref.set_in_cache()
class Service(AclMixin, models.Model):
"""Liste des services affichés sur la page d'accueil : url, description,
image et nom"""
"""Service displayed on the home page.
Attributes:
name: the name of the service.
url: the URL of the service.
description: the description of the service.
image: an image to illustrate the service (e.g. logo).
"""
name = models.CharField(max_length=32)
url = models.URLField()
@ -520,7 +616,12 @@ class Service(AclMixin, models.Model):
class MailContact(AclMixin, models.Model):
"""Contact email adress with a commentary."""
"""Contact email address with a comment.
Attributes:
address: the contact email address.
commentary: a comment used to describe the contact email address.
"""
address = models.EmailField(
default="contact@example.org", help_text=_("Contact email address.")
@ -549,6 +650,15 @@ class MailContact(AclMixin, models.Model):
class Mandate(RevMixin, AclMixin, models.Model):
"""Mandate, documenting who was the president of the organisation at a
given time.
Attributes:
president: User, the president during the mandate.
start_date: datetime, the date when the mandate started.
end_date: datetime, the date when the mandate ended.
"""
class Meta:
verbose_name = _("mandate")
verbose_name_plural = _("mandates")
@ -567,7 +677,14 @@ class Mandate(RevMixin, AclMixin, models.Model):
@classmethod
def get_mandate(cls, date=timezone.now):
""""Find the mandate taking place at the given date."""
""""Get the mandate taking place at the given date.
Args:
date: the date used to find the mandate (default: timezone.now).
Returns:
The mandate related to the given date.
"""
if callable(date):
date = date()
mandate = (
@ -590,7 +707,21 @@ class Mandate(RevMixin, AclMixin, models.Model):
class AssoOption(AclMixin, PreferencesModel):
"""Options générales de l'asso : siret, addresse, nom, etc"""
"""Information about the organisation: name, address, SIRET number etc.
Attributes:
name: the name of the organisation.
siret: the SIRET number of the organisation.
adresse1: the first line of the organisation's address, e.g. street and
number.
adresse2: the second line of the organisation's address, e.g. city and
postal code.
contact: contact email address.
telephone: contact telephone number.
pseudo: short name of the organisation.
utilisateur_asso: the user used to manage the organisation.
description: the description of the organisation.
"""
name = models.CharField(
default=_("Networking organisation school Something"), max_length=256
@ -613,13 +744,20 @@ class AssoOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=AssoOption)
def assooption_post_save(**kwargs):
"""Ecriture dans le cache"""
"""Write in the cache."""
asso_pref = kwargs["instance"]
asso_pref.set_in_cache()
class HomeOption(AclMixin, PreferencesModel):
"""Settings of the home page (facebook/twitter etc)"""
"""Social networks displayed on the home page (supports only Facebook and
Twitter).
Attributes:
facebook_url: URL of the Facebook account.
twitter_url: URL of the Twitter account.
twitter_account_name: name of the Twitter account.
"""
facebook_url = models.URLField(null=True, blank=True)
twitter_url = models.URLField(null=True, blank=True)
@ -632,13 +770,18 @@ class HomeOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs):
"""Ecriture dans le cache"""
"""Write in the cache."""
home_pref = kwargs["instance"]
home_pref.set_in_cache()
class MailMessageOption(AclMixin, models.Model):
"""Reglages, mail de bienvenue et autre"""
"""Welcome email messages preferences.
Attributes:
welcome_mail_fr: the text of the welcome email in French.
welcome_mail_en: the text of the welcome email in English.
"""
welcome_mail_fr = models.TextField(
default="", blank=True, help_text=_("Welcome email in French.")
@ -655,6 +798,14 @@ class MailMessageOption(AclMixin, models.Model):
class RadiusAttribute(RevMixin, AclMixin, models.Model):
"""RADIUS attributes preferences.
Attributes:
attribute: the name of the RADIUS attribute.
value: the value of the RADIUS attribute.
comment: the comment to document the attribute.
"""
class Meta:
verbose_name = _("RADIUS attribute")
verbose_name_plural = _("RADIUS attributes")
@ -677,6 +828,30 @@ class RadiusAttribute(RevMixin, AclMixin, models.Model):
class RadiusOption(AclMixin, PreferencesModel):
"""RADIUS preferences.
Attributes:
radius_general_policy: the general RADIUS policy (MACHINE or DEFINED).
unknown_machine: the RADIUS policy for unknown machines.
unknown_machine_vlan: the VLAN for unknown machines if not rejected.
unknown_machine_attributes: the answer attributes for unknown machines.
unknown_port: the RADIUS policy for unknown ports.
unknown_port_vlan: the VLAN for unknown ports if not rejected;
unknown_port_attributes: the answer attributes for unknown ports.
unknown_room: the RADIUS policy for machines connecting from
unregistered rooms (relevant for ports with STRICT RADIUS mode).
unknown_room_vlan: the VLAN for unknown rooms if not rejected.
unknown_room_attributes: the answer attributes for unknown rooms.
non_member: the RADIUS policy for non members.
non_member_vlan: the VLAN for non members if not rejected.
non_member_attributes: the answer attributes for non members.
banned: the RADIUS policy for banned users.
banned_vlan: the VLAN for banned users if not rejected.
banned_attributes: the answer attributes for banned users.
vlan_decision_ok: the VLAN for accepted machines.
ok_attributes: the answer attributes for accepted machines.
"""
class Meta:
verbose_name = _("RADIUS policy")
verbose_name_plural = _("RADIUS policies")
@ -847,6 +1022,15 @@ def default_voucher():
class CotisationsOption(AclMixin, PreferencesModel):
"""Subscription preferences.
Attributes:
invoice_template: the template for invoices.
voucher_template: the template for vouchers.
send_voucher_mail: whether the voucher is sent by email when the
invoice is controlled.
"""
class Meta:
verbose_name = _("subscription preferences")
@ -877,6 +1061,10 @@ class CotisationsOption(AclMixin, PreferencesModel):
class DocumentTemplate(RevMixin, AclMixin, models.Model):
"""Represent a template in order to create documents such as invoice or
subscription voucher.
Attributes:
template: file, the template used to create documents.
name: the name of the template.
"""
template = models.FileField(upload_to="templates/", verbose_name=_("template"))
@ -892,9 +1080,8 @@ class DocumentTemplate(RevMixin, AclMixin, models.Model):
@receiver(models.signals.post_delete, sender=DocumentTemplate)
def auto_delete_file_on_delete(sender, instance, **kwargs):
"""
Deletes file from filesystem
when corresponding `DocumentTemplate` object is deleted.
"""Delete the tempalte file from filesystem when the related
DocumentTemplate object is deleted.
"""
if instance.template:
if os.path.isfile(instance.template.path):
@ -903,10 +1090,8 @@ def auto_delete_file_on_delete(sender, instance, **kwargs):
@receiver(models.signals.pre_save, sender=DocumentTemplate)
def auto_delete_file_on_change(sender, instance, **kwargs):
"""
Deletes old file from filesystem
when corresponding `DocumentTemplate` object is updated
with new file.
"""Delete the previous file from filesystem when the related
DocumentTemplate object is updated with new file.
"""
if not instance.pk:
return False

View file

@ -24,8 +24,8 @@
# Gabriel Détraz, Augustin Lemesle
# Gplv2
"""
Vue d'affichage, et de modification des réglages (réglages machine,
topologie, users, service...)
Views to display and edit settings (preferences of machines, users, topology,
services etc.)
"""
from __future__ import unicode_literals
@ -88,7 +88,7 @@ from . import forms
def edit_options_template_function(request, section, forms, models):
""" Edition des préférences générales"""
"""View used to edit general preferences."""
model = getattr(models, section, None)
form_instance = getattr(forms, "Edit" + section + "Form", None)
if not (model or form_instance):
@ -127,8 +127,7 @@ def edit_options_template_function(request, section, forms, models):
HomeOption,
)
def display_options(request):
"""Vue pour affichage des options (en vrac) classé selon les models
correspondants dans un tableau"""
"""View used to display preferences sorted by model."""
useroptions, _created = OptionalUser.objects.get_or_create()
machineoptions, _created = OptionalMachine.objects.get_or_create()
topologieoptions, _created = OptionalTopologie.objects.get_or_create()
@ -188,7 +187,7 @@ def edit_options(request, section):
@login_required
@can_create(Service)
def add_service(request):
"""Ajout d'un service de la page d'accueil"""
"""View used to add services displayed on the home page."""
service = ServiceForm(request.POST or None, request.FILES or None)
if service.is_valid():
service.save()
@ -204,7 +203,7 @@ def add_service(request):
@login_required
@can_edit(Service)
def edit_service(request, service_instance, **_kwargs):
"""Edition des services affichés sur la page d'accueil"""
"""View used to edit services displayed on the home page."""
service = ServiceForm(
request.POST or None, request.FILES or None, instance=service_instance
)
@ -222,7 +221,7 @@ def edit_service(request, service_instance, **_kwargs):
@login_required
@can_delete(Service)
def del_service(request, service_instance, **_kwargs):
"""Suppression d'un service de la page d'accueil"""
"""View used to delete services displayed on the home page."""
if request.method == "POST":
service_instance.delete()
messages.success(request, _("The service was deleted."))
@ -237,7 +236,7 @@ def del_service(request, service_instance, **_kwargs):
@login_required
@can_create(Reminder)
def add_reminder(request):
"""Ajout d'un mail de rappel"""
"""View used to add reminders."""
reminder = ReminderForm(request.POST or None, request.FILES or None)
if reminder.is_valid():
reminder.save()
@ -253,7 +252,7 @@ def add_reminder(request):
@login_required
@can_edit(Reminder)
def edit_reminder(request, reminder_instance, **_kwargs):
"""Edition reminder"""
"""View used to edit reminders."""
reminder = ReminderForm(
request.POST or None, request.FILES or None, instance=reminder_instance
)
@ -271,7 +270,7 @@ def edit_reminder(request, reminder_instance, **_kwargs):
@login_required
@can_delete(Reminder)
def del_reminder(request, reminder_instance, **_kwargs):
"""Destruction d'un reminder"""
"""View used to delete reminders."""
if request.method == "POST":
reminder_instance.delete()
messages.success(request, _("The reminder was deleted."))
@ -286,7 +285,7 @@ def del_reminder(request, reminder_instance, **_kwargs):
@login_required
@can_create(RadiusKey)
def add_radiuskey(request):
"""Ajout d'une clef radius"""
"""View used to add RADIUS keys."""
radiuskey = RadiusKeyForm(request.POST or None)
if radiuskey.is_valid():
radiuskey.save()
@ -301,7 +300,7 @@ def add_radiuskey(request):
@can_edit(RadiusKey)
def edit_radiuskey(request, radiuskey_instance, **_kwargs):
"""Edition des clefs radius"""
"""View used to edit RADIUS keys."""
radiuskey = RadiusKeyForm(request.POST or None, instance=radiuskey_instance)
if radiuskey.is_valid():
radiuskey.save()
@ -317,7 +316,7 @@ def edit_radiuskey(request, radiuskey_instance, **_kwargs):
@login_required
@can_delete(RadiusKey)
def del_radiuskey(request, radiuskey_instance, **_kwargs):
"""Destruction d'un radiuskey"""
"""View used to delete RADIUS keys."""
if request.method == "POST":
try:
radiuskey_instance.delete()
@ -341,7 +340,7 @@ def del_radiuskey(request, radiuskey_instance, **_kwargs):
@login_required
@can_create(SwitchManagementCred)
def add_switchmanagementcred(request):
"""Ajout de creds de management"""
"""View used to add switch management credentials."""
switchmanagementcred = SwitchManagementCredForm(request.POST or None)
if switchmanagementcred.is_valid():
switchmanagementcred.save()
@ -356,7 +355,7 @@ def add_switchmanagementcred(request):
@can_edit(SwitchManagementCred)
def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs):
"""Edition des creds de management"""
"""View used to edit switch management credentials."""
switchmanagementcred = SwitchManagementCredForm(
request.POST or None, instance=switchmanagementcred_instance
)
@ -374,7 +373,7 @@ def edit_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs)
@login_required
@can_delete(SwitchManagementCred)
def del_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs):
"""Destruction d'un switchmanagementcred"""
"""View used to delete switch management credentials."""
if request.method == "POST":
try:
switchmanagementcred_instance.delete()
@ -404,7 +403,7 @@ def del_switchmanagementcred(request, switchmanagementcred_instance, **_kwargs):
@login_required
@can_create(MailContact)
def add_mailcontact(request):
"""Add a contact email adress."""
"""View used to add contact email addresses."""
mailcontact = MailContactForm(request.POST or None, request.FILES or None)
if mailcontact.is_valid():
mailcontact.save()
@ -420,7 +419,7 @@ def add_mailcontact(request):
@login_required
@can_edit(MailContact)
def edit_mailcontact(request, mailcontact_instance, **_kwargs):
"""Edit contact email adress."""
"""View used to edit contact email addresses."""
mailcontact = MailContactForm(
request.POST or None, request.FILES or None, instance=mailcontact_instance
)
@ -438,7 +437,7 @@ def edit_mailcontact(request, mailcontact_instance, **_kwargs):
@login_required
@can_delete_set(MailContact)
def del_mailcontact(request, instances):
"""Delete an email adress"""
"""View used to delete one or several contact email addresses."""
mailcontacts = DelMailContactForm(request.POST or None, instances=instances)
if mailcontacts.is_valid():
mailcontacts_dels = mailcontacts.cleaned_data["mailcontacts"]
@ -456,9 +455,7 @@ def del_mailcontact(request, instances):
@login_required
@can_create(DocumentTemplate)
def add_document_template(request):
"""
View used to add a document template.
"""
"""View used to add document templates."""
document_template = DocumentTemplateForm(
request.POST or None, request.FILES or None
)
@ -480,9 +477,7 @@ def add_document_template(request):
@login_required
@can_edit(DocumentTemplate)
def edit_document_template(request, document_template_instance, **_kwargs):
"""
View used to edit a document_template.
"""
"""View used to edit document templates."""
document_template = DocumentTemplateForm(
request.POST or None, request.FILES or None, instance=document_template_instance
)
@ -505,9 +500,7 @@ def edit_document_template(request, document_template_instance, **_kwargs):
@login_required
@can_delete_set(DocumentTemplate)
def del_document_template(request, instances):
"""
View used to delete a set of document template.
"""
"""View used to delete one or several document templates."""
document_template = DelDocumentTemplateForm(
request.POST or None, instances=instances
)
@ -545,7 +538,7 @@ def del_document_template(request, instances):
@login_required
@can_create(RadiusAttribute)
def add_radiusattribute(request):
"""Create a RADIUS attribute."""
"""View used to add RADIUS attributes."""
attribute = RadiusAttributeForm(request.POST or None)
if attribute.is_valid():
attribute.save()
@ -561,7 +554,7 @@ def add_radiusattribute(request):
@login_required
@can_edit(RadiusAttribute)
def edit_radiusattribute(request, radiusattribute_instance, **_kwargs):
"""Edit a RADIUS attribute."""
"""View used to edit RADIUS attributes."""
attribute = RadiusAttributeForm(
request.POST or None, instance=radiusattribute_instance
)
@ -579,7 +572,7 @@ def edit_radiusattribute(request, radiusattribute_instance, **_kwargs):
@login_required
@can_delete(RadiusAttribute)
def del_radiusattribute(request, radiusattribute_instance, **_kwargs):
"""Delete a RADIUS attribute."""
"""View used to delete RADIUS attributes."""
if request.method == "POST":
radiusattribute_instance.delete()
messages.success(request, _("The attribute was deleted."))
@ -594,7 +587,7 @@ def del_radiusattribute(request, radiusattribute_instance, **_kwargs):
@login_required
@can_create(Mandate)
def add_mandate(request):
"""Create a mandate."""
"""View used to add mandates."""
mandate = MandateForm(request.POST or None)
if mandate.is_valid():
mandate.save()
@ -610,7 +603,7 @@ def add_mandate(request):
@login_required
@can_edit(Mandate)
def edit_mandate(request, mandate_instance, **_kwargs):
"""Edit a mandate."""
"""View used to edit mandates."""
mandate = MandateForm(request.POST or None, instance=mandate_instance)
if mandate.is_valid():
mandate.save()
@ -626,7 +619,7 @@ def edit_mandate(request, mandate_instance, **_kwargs):
@login_required
@can_delete(Mandate)
def del_mandate(request, mandate_instance, **_kwargs):
"""Delete a mandate."""
"""View used to delete mandates."""
if request.method == "POST":
mandate_instance.delete()
messages.success(request, _("The mandate was deleted."))

View file

@ -21,9 +21,7 @@
# -*- coding: utf-8 -*-
"""
Regroupe les fonctions transversales utiles
Et non corrélées/dépendantes des autres applications
Global independant usefull functions
"""
import smtplib

View file

@ -19,7 +19,7 @@
# 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.
"""Fonction de context, variables renvoyées à toutes les vues"""
"""Context functions, runs and results sends globaly to all templates"""
from __future__ import unicode_literals
@ -34,8 +34,12 @@ from re2o.settings_local import OPTIONNAL_APPS_RE2O
def context_user(request):
"""Fonction de context lorsqu'un user est logué (ou non),
renvoie les infos sur l'user, la liste de ses droits, ses machines"""
"""Global Context function
Returns:
dict:Containing user's interfaces and himself if logged, else None
"""
user = request.user
if get_language() == "fr":
global_message = GeneralOption.get_cached_value("general_message_fr")
@ -61,8 +65,13 @@ def context_user(request):
def context_optionnal_apps(request):
"""Fonction de context pour générer la navbar en fonction des
apps optionnels"""
"""Context functions. Called to add optionnal apps buttons in navbari
Returns:
dict:Containing optionnal template list of functions for navbar found
in optional apps
"""
optionnal_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_navbar_user_list = [
app.views.navbar_user()

View file

@ -85,7 +85,13 @@ class FieldPermissionModelMixin:
class FieldPermissionFormMixin:
"""
Construit le formulaire et retire les champs interdits
Build a form, and remove all forbiden fields
Parameters:
user:Build-in with a Django Form instance, and parameter user in kwargs,
representing calling user for this form. Then test if a field is forbiden
or not with has_field_paremeter model function
"""
def __init__(self, *args, **kwargs):

View file

@ -45,7 +45,14 @@ DIGEST_LEN = 20
def makeSecret(password):
""" Build a hashed and salted version of the password """
""" Build a hashed and salted version of the password with SSHA
Parameters:
password (string): Password to hash
Returns:
string: Hashed password
"""
salt = os.urandom(4)
h = hashlib.sha1(password.encode())
h.update(salt)
@ -53,13 +60,30 @@ def makeSecret(password):
def hashNT(password):
""" Build a md4 hash of the password to use as the NT-password """
""" Build a md4 hash of the password to use as the NT-password
Parameters:
password (string): Password to hash
Returns:
string: Hashed password
"""
hash_str = hashlib.new("md4", password.encode("utf-16le")).digest()
return binascii.hexlify(hash_str).upper()
def checkPassword(challenge_password, password):
""" Check if a given password match the hash of a stored password """
"""Check if a given password match the hash of a stored password
Parameters:
challenge_password (string): Password to verify with hash
password (string): Hashed password to verify
Returns:
boolean: True if challenge_password and password match
"""
challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode())
digest = challenge_bytes[:DIGEST_LEN]
salt = challenge_bytes[DIGEST_LEN:]
@ -69,7 +93,15 @@ def checkPassword(challenge_password, password):
def hash_password_salt(hashed_password):
""" Extract the salt from a given hashed password """
""" Extract the salt from a given hashed password
Parameters:
hashed_password (string): Hashed password to extract salt
Returns:
string: Salt of the password
"""
if hashed_password.upper().startswith("{CRYPT}"):
hashed_password = hashed_password[7:]
if hashed_password.startswith("$"):
@ -243,6 +275,14 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
class RecryptBackend(ModelBackend):
"""Function for legacy users. During auth, if their hash password is different from SSHA or ntlm
password is empty, rehash in SSHA or NTLM
Returns:
model user instance: Instance of the user logged
"""
def authenticate(self, username=None, password=None):
# we obtain from the classical auth backend the user
user = super(RecryptBackend, self).authenticate(None, username, password)

View file

@ -22,7 +22,7 @@
# -*- coding: utf-8 -*-
# Jean-Romain Garnier
"""
Regroupe les fonctions en lien avec les mails
All functions linked with emails here. Non model or app dependant
"""
from django.utils.translation import ugettext_lazy as _

View file

@ -93,16 +93,27 @@ class AclMixin(object):
@classmethod
def get_instance(cls, object_id, *_args, **kwargs):
"""Récupère une instance
:return: Une instance de la classe évidemment"""
"""Get an instance from its id.
Parameters:
object_id (int): Id of the instance to find
Returns:
Django instance: Instance of this class
"""
return cls.objects.get(pk=object_id)
@classmethod
def can_create(cls, user_request, *_args, **_kwargs):
"""Verifie que l'user a les bons droits pour créer
un object
:param user_request: instance utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec"""
"""Check if a user has the right to create 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() + ".add_" + cls.get_classname()
can = user_request.has_perm(permission)
return (
@ -114,11 +125,16 @@ class AclMixin(object):
)
def can_edit(self, user_request, *_args, **_kwargs):
"""Verifie que l'user a les bons droits pour editer
cette instance
:param self: Instance à editer
:param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec"""
"""Check if a user has the right to edit an instance
Parameters:
user_request: User calling for this action
self: Instance to edit
Returns:
Boolean: True if user_request has the right access to do it, else
false with reason for reject authorization
"""
permission = self.get_modulename() + ".change_" + self.get_classname()
can = user_request.has_perm(permission)
return (
@ -130,11 +146,16 @@ class AclMixin(object):
)
def can_delete(self, user_request, *_args, **_kwargs):
"""Verifie que l'user a les bons droits pour delete
cette instance
:param self: Instance à delete
:param user_request: Utilisateur qui fait la requête
:return: soit True, soit False avec la raison de l'échec"""
"""Check if a user has the right to delete an instance
Parameters:
user_request: User calling for this action
self: Instance to delete
Returns:
Boolean: True if user_request has the right access to do it, else
false with reason for reject authorization
"""
permission = self.get_modulename() + ".delete_" + self.get_classname()
can = user_request.has_perm(permission)
return (
@ -147,10 +168,15 @@ class AclMixin(object):
@classmethod
def can_view_all(cls, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien afficher l'ensemble des objets,
droit particulier view objet correspondant
:param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant"""
"""Check if a user can view 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 (
@ -162,11 +188,16 @@ class AclMixin(object):
)
def can_view(self, user_request, *_args, **_kwargs):
"""Vérifie qu'on peut bien voir cette instance particulière avec
droit view objet
:param self: instance à voir
:param user_request: instance user qui fait l'edition
:return: True ou False avec la raison de l'échec le cas échéant"""
"""Check if a user can view an instance of an object
Parameters:
user_request: User calling for this action
self: Instance to view
Returns:
Boolean: True if user_request has the right access to do it, else
false with reason for reject authorization
"""
permission = self.get_modulename() + ".view_" + self.get_classname()
can = user_request.has_perm(permission)
return (

View file

@ -47,7 +47,15 @@ application = get_wsgi_application()
def get_user(pseudo):
"""Cherche un utilisateur re2o à partir de son pseudo"""
"""Find a user from its pseudo
Parameters:
pseudo (string): pseudo of this user
Returns:
user instance:Instance of user
"""
user = User.objects.filter(pseudo=pseudo)
if len(user) == 0:
raise CommandError("Invalid user.")
@ -59,17 +67,20 @@ def get_user(pseudo):
def get_system_user():
"""Retourne l'utilisateur système ayant lancé la commande"""
"""Find the system user login who used the command
"""
return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
def form_cli(Form, user, action, *args, **kwargs):
"""
Remplit un formulaire à partir de la ligne de commande
Form : le formulaire (sous forme de classe) à remplir
user : l'utilisateur re2o faisant la modification
action : l'action réalisée par le formulaire (pour les logs)
Les arguments suivants sont transmis tels quels au formulaire.
Fill-in a django form from cli
Parameters
Form : a django class form to fill-in
user : a re2o user doign the modification
action: the action done with that form, for logs purpose
"""
data = {}
dumb_form = Form(user=user, *args, **kwargs)

View file

@ -24,12 +24,12 @@
# -*- coding: utf-8 -*-
# David Sinquin, Gabriel Détraz, Lara Kermarec
"""
Regroupe les fonctions transversales utiles
A group of very usefull functions for re2o core
Fonction :
- récupérer tous les utilisateurs actifs
- récupérer toutes les machines
- récupérer tous les bans
Functions:
- find all active users
- find all active interfaces
- find all bans
etc
"""
@ -47,7 +47,14 @@ from preferences.models import AssoOption
def get_group_having_permission(*permission_name):
"""Returns every group having the permission `permission_name`
"""Return all django groups having this permission
Parameters:
permission name (string): Permission name
Returns:
re2o groups: Groups having this permission
"""
groups = set()
for name in permission_name:
@ -60,10 +67,19 @@ def get_group_having_permission(*permission_name):
def all_adherent(search_time=None, including_asso=True):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est
qu'une seule requete sql
Inspecte les factures de l'user et ses cotisation, regarde si elles
sont posterieur à now (end_time)"""
"""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.
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 membership
"""
if search_time is None:
search_time = timezone.now()
filter_user = Q(
@ -86,7 +102,18 @@ def all_adherent(search_time=None, including_asso=True):
def all_baned(search_time=None):
""" Fonction renvoyant tous les users bannis """
"""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.
Parameters:
search_time (django datetime): Datetime to perform this search,
if not provided, search_time will be set à timezone.now()
Returns:
django queryset: Django queryset containing all users banned
"""
if search_time is None:
search_time = timezone.now()
return User.objects.filter(
@ -97,7 +124,18 @@ def all_baned(search_time=None):
def all_whitelisted(search_time=None):
""" Fonction renvoyant tous les users whitelistes """
"""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.
Parameters:
search_time (django datetime): Datetime to perform this search,
if not provided, search_time will be set à timezone.now()
Returns:
django queryset: Django queryset containing all users whitelisted
"""
if search_time is None:
search_time = timezone.now()
return User.objects.filter(
@ -108,11 +146,19 @@ def all_whitelisted(search_time=None):
def all_has_access(search_time=None, including_asso=True):
""" Return all connected users : active users and whitelisted +
asso_user defined in AssoOption pannel
----
Renvoie tous les users beneficiant d'une connexion
: user adherent et whiteliste non banni plus l'utilisateur asso"""
"""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
with a whitelist, or a valid paid access, except banned users.
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 valid connection users
"""
if search_time is None:
search_time = timezone.now()
filter_user = (
@ -153,7 +199,20 @@ def all_has_access(search_time=None, including_asso=True):
def filter_active_interfaces(interface_set):
"""Filtre les machines autorisées à sortir sur internet dans une requête"""
"""Return a filter for filtering all interfaces of people who have an valid
internet access at org.
Call all_active_interfaces and then apply filter of theses active users on an
interfaces_set
Parameters:
interface_set (django queryset): A queryset of interfaces to perform filter
Returns:
django filter: Django filter to apply to an interfaces queryset,
will return when applied all active interfaces, related with
a user with valid membership
"""
return (
interface_set.filter(
machine__in=Machine.objects.filter(user__in=all_has_access()).filter(
@ -171,12 +230,38 @@ def filter_active_interfaces(interface_set):
def filter_complete_interfaces(interface_set):
"""Appel la fonction précédente avec un prefetch_related ipv6 en plus"""
"""Return a filter for filtering all interfaces of people who have an valid
internet access at org.
Call all_active_interfaces and then apply filter of theses active users on an
interfaces_set. Less efficient than filter_active_interfaces, with a prefetch_related
on ipv6
Parameters:
interface_set (django queryset): A queryset of interfaces to perform filter
Returns:
django filter: Django filter to apply to an interfaces queryset,
will return when applied all active interfaces, related with
a user with valid membership
"""
return filter_active_interfaces(interface_set).prefetch_related("ipv6list")
def all_active_interfaces(full=False):
"""Renvoie l'ensemble des machines autorisées à sortir sur internet """
"""Return a filter for filtering all interfaces of people who have an valid
internet access at org.
Call filter_active_interfaces or filter_complete_interfaces.
Parameters:
full (boolean): A queryset of interfaces to perform filter. If true, will perform
a complete filter with filter_complete_interfaces
Returns:
django queryset: Django queryset containing all active interfaces, related with
a user with valid membership
"""
if full:
return filter_complete_interfaces(Interface.objects)
else:
@ -184,13 +269,30 @@ def all_active_interfaces(full=False):
def all_active_assigned_interfaces(full=False):
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et
disposant de l'accès internet"""
"""Return all interfaces of people who have an valid internet access at org,
and with valid ipv4.
Call filter_active_interfaces or filter_complete_interfaces, with parameter full.
Parameters:
full (boolean): A queryset of interfaces to perform filter. If true, will perform
a complete filter with filter_complete_interfaces
Returns:
django queryset: Django queryset containing all active interfaces, related with
a user with valid membership, and with valid assigned ipv4 address
"""
return all_active_interfaces(full=full).filter(ipv4__isnull=False)
def all_active_interfaces_count():
""" Version light seulement pour compter"""
"""Counts all interfaces of people who have an valid internet access at org.
Returns:
int: Number of all active interfaces, related with
a user with valid membership.
"""
return Interface.objects.filter(
machine__in=Machine.objects.filter(user__in=all_has_access()).filter(
active=True
@ -199,12 +301,26 @@ def all_active_interfaces_count():
def all_active_assigned_interfaces_count():
""" Version light seulement pour compter"""
"""Counts all interfaces of people who have an valid internet access at org,
and with valid ipv4.
Returns:
int: Number of all active interfaces, related with
a user with valid membership, and with valid assigned ipv4 address
"""
return all_active_interfaces_count().filter(ipv4__isnull=False)
def remove_user_room(room, force=True):
""" Déménage de force l'ancien locataire de la chambre """
"""Remove the previous user of that room. If force, will not perform a check
of membership on him before doing it
Parameters:
room (Room instance): Room to make free of user
force (boolean): If true, bypass membership check
"""
try:
user = Adherent.objects.get(room=room)
except Adherent.DoesNotExist:

View file

@ -21,8 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Fonctions de la page d'accueil et diverses fonctions utiles pour tous
les views
Welcom main page view, and several template widely used in re2o views
"""
from __future__ import unicode_literals
@ -50,14 +49,29 @@ from re2o.settings_local import OPTIONNAL_APPS_RE2O
def form(ctx, template, request):
"""Form générique, raccourci importé par les fonctions views du site"""
"""Global template function, used in all re2o views, for building a render with context,
template and request. Adding csrf.
Parameters:
ctx (dict): Dict of values to transfer to template
template (django template): The django template of this view
request (django request)
Returns:
Django render: Django render complete view with template, context and request
"""
context = ctx
context.update(csrf(request))
return render(request, template, context)
def index(request):
"""Affiche la liste des services sur la page d'accueil de re2o"""
"""Display all services provided on main page
Returns: a form with all services linked and description, and social media
link if provided.
"""
services = [[], [], []]
for indice, serv in enumerate(Service.objects.all()):
services[indice % 3].append(serv)

View file

@ -45,21 +45,43 @@ 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"""
and a list of subqueries.
Attributes:
text: the string written by the user in a query.
operator: character used to link subqueries, e.g. "+".
subqueries: list of Query objects when the current query is split in
several parts.
"""
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
"""Initialise an instance of Query.
Args:
text: the content of the query (default: "").
case_sensitive: bool, True if the query is case sensitive and
False if not (default: False).
"""
self.text = text
self.operator = None
self.subqueries = None
self.case_sensitive = case_sensitive
def add_char(self, char):
"""Add the given char to the query's text"""
"""Add the given character to the query's text.
Args:
char: the character to be added.
"""
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"""
of a plain Query object.
Args:
operator: the operator to be added.
"""
self.operator = operator
if self.subqueries is None:
@ -71,7 +93,7 @@ class Query:
@property
def plaintext(self):
"""Returns a textual representation of the query's content"""
"""Return the textual representation of the query's content."""
if self.operator is not None:
return self.operator.join([q.plaintext for q in self.subqueries])
@ -82,7 +104,7 @@ class Query:
def filter_fields():
"""Return the list of fields the search applies to"""
"""Return the list of fields the search applies to."""
return ["users",
"clubs",
"machines",
@ -95,12 +117,12 @@ def filter_fields():
def empty_filters():
"""Build empty filters used by Django"""
"""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 """
"""Check if the variable can be cast to an integer."""
try:
int(variable)
except ValueError:
@ -111,8 +133,18 @@ def is_int(variable):
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"""
number of max results. Finally add the info of the maximum number of
results to the dictionary.
Args:
request: django request, corresponding to the search.
results: dict, the results of the search.
col: the column used to sort the results.
order: the order used to sort the results.
Returns:
The dictionary of results sorted and paginated.
"""
results["users"] = SortTable.sort(
results["users"], col, order, SortTable.USERS_INDEX
)
@ -156,7 +188,16 @@ def finish_results(request, results, col, order):
def contains_filter(attribute, word, case_sensitive=False):
"""Create a django model filtering whether the given attribute
contains the specified value."""
contains the specified value.
Args:
attribute: the attribute used to check if it contains the given word or
not.
word: the word used to check if it is contained in the attribute or
not.
case_sensitive: bool, True if the check is case sensitive and
False if not (default: False).
"""
if case_sensitive:
attr = "{}__{}".format(attribute, "contains")
else:
@ -168,12 +209,13 @@ def contains_filter(attribute, word, 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
"""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
The match fields 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."""
to an int.
"""
# Users
if "0" in aff:
@ -333,7 +375,7 @@ def search_single_word(word, filters, user, start, end,
def apply_filters(filters, user, aff):
""" Apply the filters constructed by search_single_query.
"""Apply the filters constructed by search_single_query.
It also takes into account the visual filters defined during
the search query.
"""
@ -406,8 +448,8 @@ def apply_filters(filters, user, 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"""
"""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()

View file

@ -62,7 +62,7 @@ def initial_choices(choice_set):
class SearchForm(Form):
"""The form for a simple search"""
"""Form used to do a simple search."""
q = forms.CharField(
label=_("Search"),
@ -78,7 +78,7 @@ class SearchForm(Form):
class SearchFormPlus(Form):
"""The form for an advanced search (with filters)"""
"""Form used to do an advanced search (with filters)."""
q = forms.CharField(
label=_("Search"),

View file

@ -83,7 +83,7 @@ def get_results(query, request, params):
@login_required
@can_view_all(User, Machine, Cotisation)
def search(request):
""" La page de recherche standard """
"""View used to display the simple search page."""
search_form = SearchForm(request.GET or None)
if search_form.is_valid():
return render(
@ -101,7 +101,7 @@ def search(request):
@login_required
@can_view_all(User, Machine, Cotisation)
def searchp(request):
""" La page de recherche avancée """
"""View used to display the advanced search page."""
search_form = SearchFormPlus(request.GET or None)
if search_form.is_valid():
return render(

View file

@ -35,7 +35,7 @@ from .models import Ticket, CommentTicket
class NewTicketForm(FormRevMixin, ModelForm):
""" Creation of a ticket"""
"""Form used to create tickets."""
class Meta:
model = Ticket
@ -53,7 +53,7 @@ class NewTicketForm(FormRevMixin, ModelForm):
class EditTicketForm(FormRevMixin, ModelForm):
""" Creation of a ticket"""
"""Form used to edit tickets."""
class Meta:
model = Ticket
@ -65,7 +65,7 @@ class EditTicketForm(FormRevMixin, ModelForm):
class CommentTicketForm(FormRevMixin, ModelForm):
"""Edit and create comment to a ticket"""
"""Form used to create and edit comments of a ticket."""
class Meta:
model = CommentTicket

View file

@ -46,7 +46,23 @@ from .preferences.models import TicketOption
class Ticket(AclMixin, models.Model):
"""Model of a ticket"""
"""Model of a ticket.
Attributes:
user: User, the user creating the ticket.
title: the title of the ticket, chosen by the user.
description: the main content of the ticket, written by the user to
explain their problem.
date: datetime, the date of creation of the ticket.
email: the email address used to reply to the ticket.
solved: boolean, True if the problem explained in the ticket has been
solved, False otherwise. It is used to see easily which tickets
still require attention.
language: the language of the ticket, used to select the appropriate
template when sending automatic emails, e.g. ticket creation.
request: the request displayed if there is an error when sending emails
related to the ticket.
"""
user = models.ForeignKey(
"users.User",
@ -86,7 +102,7 @@ class Ticket(AclMixin, models.Model):
@cached_property
def opened_by(self):
"""Return full name of this ticket opener"""
"""Get the full name of the user who opened the ticket."""
if self.user:
return self.user.get_full_name()
else:
@ -94,10 +110,13 @@ class Ticket(AclMixin, models.Model):
@cached_property
def get_mail(self):
"""Return the mail of the owner of this ticket"""
"""Get the email address of the user who opened the ticket."""
return self.email or self.user.get_mail
def publish_mail(self):
"""Send an email for a newly opened ticket to the address set in the
preferences.
"""
site_url = GeneralOption.get_cached_value("main_site_url")
to_addr = TicketOption.get_cached_value("publish_address")
context = {"ticket": self, "site_url": site_url}
@ -120,8 +139,8 @@ class Ticket(AclMixin, models.Model):
def can_view(self, user_request, *_args, **_kwargs):
""" Check that the user has the right to view the ticket
or that it is the author"""
"""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_ticket")
and self.user != user_request
@ -136,7 +155,7 @@ 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"""
"""Check that the user has access to the list of all tickets."""
can = user_request.has_perm("tickets.view_ticket")
return (
can,
@ -147,12 +166,23 @@ class Ticket(AclMixin, models.Model):
)
def can_create(user_request, *_args, **_kwargs):
""" Authorise all users to open tickets """
"""Authorise all users to open tickets."""
return True, None, None
class CommentTicket(AclMixin, models.Model):
"""A comment of a ticket"""
"""A comment of a ticket.
Attributes:
date: datetime, the date of creation of the comment.
comment: the text written as a comment to a ticket.
parent_ticket: the ticket which is commented.
created_at: datetime, the date of creation of the comment.
created_by: the user who wrote the comment.
request: the request used if there is an error when sending emails
related to the comment.
"""
date = models.DateTimeField(auto_now_add=True)
comment = models.TextField(
max_length=4095,
@ -180,8 +210,8 @@ class CommentTicket(AclMixin, models.Model):
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"""
"""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
@ -195,8 +225,8 @@ class CommentTicket(AclMixin, models.Model):
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"""
"""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.change_commentticket")
and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by)
@ -211,7 +241,7 @@ class CommentTicket(AclMixin, models.Model):
@staticmethod
def can_view_all(user_request, *_args, **_kwargs):
""" Check that the user has access to the list of all tickets comments"""
"""Check that the user has access to the list of all tickets comments."""
can = user_request.has_perm("tickets.view_commentticket")
return (
can,
@ -225,7 +255,9 @@ 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"""
"""Send an email for a newly written comment to the ticket's author and
to the address set in the preferences.
"""
site_url = GeneralOption.get_cached_value("main_site_url")
to_addr = TicketOption.get_cached_value("publish_address")
context = {"comment": self, "site_url": site_url}
@ -246,7 +278,7 @@ class CommentTicket(AclMixin, models.Model):
@receiver(post_save, sender=Ticket)
def ticket_post_save(**kwargs):
""" Send the mail to publish the new ticket """
"""Call the method to publish an email when a ticket is created."""
if kwargs["created"]:
if TicketOption.get_cached_value("publish_address"):
ticket = kwargs["instance"]
@ -255,7 +287,7 @@ def ticket_post_save(**kwargs):
@receiver(post_save, sender=CommentTicket)
def comment_post_save(**kwargs):
""" Send the mail to publish the new comment """
"""Call the method to publish an email when a comment is created."""
if kwargs["created"]:
if TicketOption.get_cached_value("publish_address"):
comment = kwargs["instance"]

View file

@ -32,7 +32,7 @@ from .models import TicketOption
class EditTicketOptionForm(FormRevMixin, ModelForm):
""" Edit the ticket's settings"""
"""Form used to edit the settings of tickets."""
class Meta:
model = TicketOption

View file

@ -32,7 +32,7 @@ from preferences.models import PreferencesModel
class TicketOption(AclMixin, PreferencesModel):
""" Definition of the ticket's settings"""
"""Definition of the settings of tickets."""
publish_address = models.EmailField(
help_text=_(

View file

@ -42,7 +42,7 @@ from . import models
def aff_preferences(request):
""" View to display the settings of the tickets in the preferences page"""
"""View used to display the settings of tickets in the preferences page."""
pref, created = models.TicketOption.objects.get_or_create()
context = {
"preferences": pref,

View file

@ -20,10 +20,6 @@
# 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
@ -52,7 +48,7 @@ from .forms import NewTicketForm, EditTicketForm, CommentTicketForm
def new_ticket(request):
""" Ticket creation view"""
"""View used to display the creation form of tickets."""
ticketform = NewTicketForm(request.POST or None, request=request)
if ticketform.is_valid():
ticketform.save()
@ -76,7 +72,7 @@ def new_ticket(request):
@login_required
@can_view(Ticket)
def aff_ticket(request, ticket, ticketid):
"""View to display only one ticket"""
"""View used to display a single ticket."""
comments = CommentTicket.objects.filter(parent_ticket=ticket)
return render(
request,
@ -88,7 +84,7 @@ def aff_ticket(request, ticket, ticketid):
@login_required
@can_edit(Ticket)
def change_ticket_status(request, ticket, ticketid):
"""View to edit ticket state"""
"""View used to change a ticket's status."""
ticket.solved = not ticket.solved
ticket.save()
return redirect(
@ -99,7 +95,7 @@ def change_ticket_status(request, ticket, ticketid):
@login_required
@can_edit(Ticket)
def edit_ticket(request, ticket, ticketid):
""" Ticket creation view"""
"""View used to display the edit form of tickets."""
ticketform = EditTicketForm(request.POST or None, instance=ticket)
if ticketform.is_valid():
ticketform.save()
@ -120,7 +116,7 @@ def edit_ticket(request, ticket, ticketid):
@login_required
@can_view(Ticket)
def add_comment(request, ticket, ticketid):
""" Add a comment to a ticket"""
"""View used to add a comment to a ticket."""
commentticket = CommentTicketForm(request.POST or None, request=request)
if commentticket.is_valid():
commentticket = commentticket.save(commit=False)
@ -139,7 +135,7 @@ def add_comment(request, ticket, ticketid):
@login_required
@can_edit(CommentTicket)
def edit_comment(request, commentticket_instance, **_kwargs):
""" Edit a comment of a ticket"""
"""View used to 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
@ -157,7 +153,7 @@ def edit_comment(request, commentticket_instance, **_kwargs):
@login_required
@can_delete(CommentTicket)
def del_comment(request, commentticket, **_kwargs):
"""Delete a comment of a ticket"""
"""View used to delete a comment of a ticket."""
if request.method == "POST":
ticketid = commentticket.parent_ticket.id
commentticket.delete()
@ -173,7 +169,7 @@ def del_comment(request, commentticket, **_kwargs):
@login_required
@can_view_all(Ticket)
def aff_tickets(request):
""" View to display all the tickets """
"""View used to display all tickets."""
tickets_list = Ticket.objects.all().order_by("-date")
nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).count()
@ -196,9 +192,9 @@ def aff_tickets(request):
return render(request, "tickets/index.html", context=context)
# views cannoniques des apps optionnels
# Canonic views for optional apps
def profil(request, user):
""" View to display the ticket's module on the profil"""
"""View used to display the tickets on a user's profile."""
tickets_list = Ticket.objects.filter(user=user).all().order_by("-date")
nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).count()
@ -223,16 +219,19 @@ def profil(request, user):
def contact(request):
"""View to display a contact address on the contact page
used here to display a link to open a ticket"""
"""View used to display contact addresses to be used for tickets on the
contact page.
"""
return render_to_string("tickets/contact.html")
def navbar_user():
"""View to display the ticket link in thet user's dropdown in the navbar"""
"""View used to display a link to tickets in the navbar (in the dropdown
menu Users).
"""
return ("users", render_to_string("tickets/navbar.html"))
def navbar_logout():
"""View to display the ticket link to log out users"""
"""View used to display a link to open tickets for logged out users."""
return render_to_string("tickets/navbar_logout.html")

View file

@ -20,8 +20,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.
"""
Fichier définissant les administration des models dans l'interface admin
"""topologie.admin
The objects, fields and datastructures visible in the Django admin view.
"""
from __future__ import unicode_literals
@ -45,67 +45,67 @@ from .models import (
class StackAdmin(VersionAdmin):
"""Administration d'une stack de switches (inclus des switches)"""
"""Admin class of stacks (includes switches)."""
pass
class SwitchAdmin(VersionAdmin):
"""Administration d'un switch"""
"""Admin class of switches."""
pass
class PortAdmin(VersionAdmin):
"""Administration d'un port de switches"""
"""Admin class of switch ports."""
pass
class AccessPointAdmin(VersionAdmin):
"""Administration d'une borne"""
"""Admin class of APs."""
pass
class RoomAdmin(VersionAdmin):
"""Administration d'un chambre"""
"""Admin class of rooms."""
pass
class ModelSwitchAdmin(VersionAdmin):
"""Administration d'un modèle de switch"""
"""Admin class of switch models."""
pass
class ConstructorSwitchAdmin(VersionAdmin):
"""Administration d'un constructeur d'un switch"""
"""Admin class of switch constructors."""
pass
class SwitchBayAdmin(VersionAdmin):
"""Administration d'une baie de brassage"""
"""Admin class of switch bays."""
pass
class BuildingAdmin(VersionAdmin):
"""Administration d'un batiment"""
"""Admin class of buildings."""
pass
class DormitoryAdmin(VersionAdmin):
"""Administration d'une residence"""
"""Admin class of dormitories."""
pass
class PortProfileAdmin(VersionAdmin):
"""Administration of a port profile"""
"""Admin class of port profiles."""
pass

View file

@ -20,14 +20,12 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Un forms le plus simple possible pour les objets topologie de re2o.
Forms for the topologie app of re2o.
Permet de créer et supprimer : un Port de switch, relié à un switch.
Permet de créer des stacks et d'y ajouter des switchs (StackForm)
Permet de créer, supprimer et editer un switch (EditSwitchForm,
NewSwitchForm)
The forms are used to:
* create and delete switch ports, related to a switch.
* create stacks and add switches to them (StackForm).
* create, edit and delete a switch (NewSwitchForm, EditSwitchForm).
"""
from __future__ import unicode_literals
@ -59,8 +57,7 @@ from .models import (
class PortForm(FormRevMixin, ModelForm):
"""Formulaire pour la création d'un port d'un switch
Relié directement au modèle port"""
"""Form used to manage a switch's port."""
class Meta:
model = Port
@ -72,14 +69,11 @@ class PortForm(FormRevMixin, ModelForm):
class EditPortForm(FormRevMixin, ModelForm):
"""Form pour l'édition d'un port de switche : changement des reglages
radius ou vlan, ou attribution d'une chambre, autre port ou machine
"""Form used to edit a switch's port: change in RADIUS or VLANs settings,
assignement to a room, port or machine.
Un port est relié à une chambre, un autre port (uplink) ou une machine
(serveur ou borne), mutuellement exclusif
Optimisation sur les queryset pour machines et port_related pour
optimiser le temps de chargement avec select_related (vraiment
lent sans)"""
A port is related to either a room, another port (uplink) or a machine (server or AP).
"""
class Meta(PortForm.Meta):
fields = [
@ -106,8 +100,7 @@ class EditPortForm(FormRevMixin, ModelForm):
class AddPortForm(FormRevMixin, ModelForm):
"""Permet d'ajouter un port de switch. Voir EditPortForm pour plus
d'informations"""
"""Form used to add a switch's port. See EditPortForm."""
class Meta(PortForm.Meta):
fields = [
@ -139,8 +132,7 @@ class AddPortForm(FormRevMixin, ModelForm):
class StackForm(FormRevMixin, ModelForm):
"""Permet d'edition d'une stack : stack_id, et switches membres
de la stack"""
"""Form used to create and edit stacks."""
class Meta:
model = Stack
@ -152,8 +144,7 @@ class StackForm(FormRevMixin, ModelForm):
class AddAccessPointForm(NewMachineForm):
"""Formulaire pour la création d'une borne
Relié directement au modèle borne"""
"""Form used to create access points."""
class Meta:
model = AccessPoint
@ -161,7 +152,7 @@ class AddAccessPointForm(NewMachineForm):
class EditAccessPointForm(EditMachineForm):
"""Edition d'une borne. Edition complète"""
"""Form used to edit access points."""
class Meta:
model = AccessPoint
@ -169,7 +160,7 @@ class EditAccessPointForm(EditMachineForm):
class EditSwitchForm(EditMachineForm):
"""Permet d'éditer un switch : nom et nombre de ports"""
"""Form used to edit switches."""
class Meta:
model = Switch
@ -177,15 +168,14 @@ class EditSwitchForm(EditMachineForm):
class NewSwitchForm(NewMachineForm):
"""Permet de créer un switch : emplacement, paramètres machine,
membre d'un stack (option), nombre de ports (number)"""
"""Form used to create a switch."""
class Meta(EditSwitchForm.Meta):
fields = ["name", "switchbay", "number", "stack", "stack_member_id"]
class EditRoomForm(FormRevMixin, ModelForm):
"""Permet d'éediter le nom et commentaire d'une prise murale"""
"""Form used to edit a room."""
class Meta:
model = Room
@ -197,14 +187,14 @@ class EditRoomForm(FormRevMixin, ModelForm):
class CreatePortsForm(forms.Form):
"""Permet de créer une liste de ports pour un switch."""
"""Form used to create switch ports lists."""
begin = forms.IntegerField(label=_("Start:"), min_value=0)
end = forms.IntegerField(label=_("End:"), min_value=0)
class EditModelSwitchForm(FormRevMixin, ModelForm):
"""Permet d'éediter un modèle de switch : nom et constructeur"""
"""Form used to edit switch models."""
members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False)
@ -226,7 +216,7 @@ class EditModelSwitchForm(FormRevMixin, ModelForm):
class EditConstructorSwitchForm(FormRevMixin, ModelForm):
"""Permet d'éediter le nom d'un constructeur"""
"""Form used to edit switch constructors."""
class Meta:
model = ConstructorSwitch
@ -238,7 +228,7 @@ class EditConstructorSwitchForm(FormRevMixin, ModelForm):
class EditSwitchBayForm(FormRevMixin, ModelForm):
"""Permet d'éditer une baie de brassage"""
"""Form used to edit switch bays."""
members = forms.ModelMultipleChoiceField(Switch.objects.all(), required=False)
@ -260,7 +250,7 @@ class EditSwitchBayForm(FormRevMixin, ModelForm):
class EditBuildingForm(FormRevMixin, ModelForm):
"""Permet d'éditer le batiment"""
"""Form used to edit buildings."""
class Meta:
model = Building
@ -272,7 +262,7 @@ class EditBuildingForm(FormRevMixin, ModelForm):
class EditDormitoryForm(FormRevMixin, ModelForm):
"""Enable dormitory edition"""
"""Form used to edit dormitories."""
class Meta:
model = Dormitory
@ -284,7 +274,7 @@ class EditDormitoryForm(FormRevMixin, ModelForm):
class EditPortProfileForm(FormRevMixin, ModelForm):
"""Form to edit a port profile"""
"""Form used to edit port profiles."""
class Meta:
model = PortProfile
@ -296,7 +286,7 @@ class EditPortProfileForm(FormRevMixin, ModelForm):
class EditModuleForm(FormRevMixin, ModelForm):
"""Add and edit module instance"""
"""Form used to add and edit switch modules."""
class Meta:
model = ModuleSwitch
@ -308,7 +298,7 @@ class EditModuleForm(FormRevMixin, ModelForm):
class EditSwitchModuleForm(FormRevMixin, ModelForm):
"""Add/edit a switch to a module"""
"""Form used to add and edit modules related to a switch."""
class Meta:
model = ModuleOnSwitch

View file

@ -21,18 +21,15 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Definition des modèles de l'application topologie.
Definition of models for the 'topologie' app.
On défini les models suivants :
- stack (id, id_min, id_max et nom) regrouppant les switches
- switch : nom, nombre de port, et interface
machine correspondante (mac, ip, etc) (voir machines.models.interface)
- Port: relié à un switch parent par foreign_key, numero du port,
relié de façon exclusive à un autre port, une machine
(serveur ou borne) ou une prise murale
- room : liste des prises murales, nom et commentaire de l'état de
la prise
The following models are defined:
* stack (grouping switches): id, id_min, id_max and name
* switch: name, number of ports, related interface and machine (MAC
address, IP address etc.) (see machines.models.interface)
* port: related to a switch by foreign_key, number of the port, related
exclusively to another port, machine (server or AP) or room outlets
* room: list of outlets, name and comments about the plug's state
"""
from __future__ import unicode_literals
@ -56,9 +53,15 @@ from re2o.mixins import AclMixin, RevMixin
class Stack(AclMixin, RevMixin, models.Model):
"""Un objet stack. Regrouppe des switchs en foreign key
,contient une id de stack, un switch id min et max dans
le stack"""
"""Switch stack.
Attributes:
name: the name of the stack.
stack_id: the ID of the stack, as a text chosen by the user.
details: the description to provide details about the stack.
member_id_min: the minimum switch ID in the stack.
member_id_max: the maximum switch ID in the stack.
"""
name = models.CharField(max_length=32, blank=True, null=True)
stack_id = models.CharField(max_length=32, unique=True)
@ -81,7 +84,7 @@ class Stack(AclMixin, RevMixin, models.Model):
super(Stack, self).save(*args, **kwargs)
def clean(self):
""" Verification que l'id_max < id_min"""
"""Check if id_max < id_min."""
if self.member_id_max < self.member_id_min:
raise ValidationError(
{"member_id_max": _("The maximum ID is less than the minimum ID.")}
@ -89,9 +92,10 @@ class Stack(AclMixin, RevMixin, models.Model):
class AccessPoint(Machine):
"""Define a wireless AP. Inherit from machines.interfaces
"""Wireless Access Point. Inherits from machines.interfaces.
Definition pour une borne wifi , hérite de machines.interfaces
Attributes:
location: the text to provide details about the AP's location.
"""
location = models.CharField(
@ -107,17 +111,16 @@ class AccessPoint(Machine):
verbose_name_plural = _("access points")
def port(self):
"""Return the queryset of ports for this device"""
"""Return the queryset of ports for this device."""
return Port.objects.filter(machine_interface__machine=self)
def switch(self):
"""Return the switch where this is plugged"""
"""Return the switch where this is plugged."""
return Switch.objects.filter(ports__machine_interface__machine=self)
def building(self):
"""
Return the building of the AP/Server (building of the switchs
connected to...)
"""Return the building of the AP/Server (building of the switches
connected to...).
"""
return Building.objects.filter(switchbay__switch=self.switch())
@ -127,7 +130,14 @@ class AccessPoint(Machine):
@classmethod
def all_ap_in(cls, building_instance):
"""Get a building as argument, returns all ap of a building"""
"""Get all the APs of the given building.
Args:
building_instance: the building used to find APs.
Returns:
The queryset of all APs in the given building.
"""
return cls.objects.filter(
interface__port__switch__switchbay__building=building_instance
)
@ -158,25 +168,24 @@ class AccessPoint(Machine):
class Server(Machine):
"""
Dummy class, to retrieve servers of a building, or get switch of a server
"""Dummy class, to retrieve servers of a building, or get switch of a
server.
"""
class Meta:
proxy = True
def port(self):
"""Return the queryset of ports for this device"""
"""Return the queryset of ports for this device."""
return Port.objects.filter(machine_interface__machine=self)
def switch(self):
"""Return the switch where this is plugged"""
"""Return the switch where this is plugged."""
return Switch.objects.filter(ports__machine_interface__machine=self)
def building(self):
"""
Return the building of the AP/Server
(building of the switchs connected to...)
"""Return the building of the AP/Server (building of the switches
connected to...).
"""
return Building.objects.filter(switchbay__switch=self.switch())
@ -186,7 +195,14 @@ class Server(Machine):
@classmethod
def all_server_in(cls, building_instance):
"""Get a building as argument, returns all server of a building"""
"""Get all the servers of the given building.
Args:
building_instance: the building used to find servers.
Returns:
The queryset of all servers in the given building.
"""
return cls.objects.filter(
interface__port__switch__switchbay__building=building_instance
).exclude(accesspoint__isnull=False)
@ -217,17 +233,19 @@ class Server(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)
relié en onetoone à une interface
Pourquoi ne pas avoir fait hériter switch de interface ?
Principalement par méconnaissance de la puissance de cette façon de faire.
Ceci étant entendu, django crée en interne un onetoone, ce qui a un
effet identique avec ce que l'on fait ici
"""Switch.
Validation au save que l'id du stack est bien dans le range id_min
id_max de la stack parente"""
Attributes:
number: the number of ports of the switch.
stack: the stack the switch is a part of.
stack_member_id: the ID of the switch in the related stack.
model: the model of the switch.
switchbay: the bay in which the switch is located.
radius_key: the RADIUS key of the switch.
management_creds: the management credentials of the switch.
automatic_provision: whether automatic provision is enabled for the
switch.
"""
number = models.PositiveIntegerField(help_text=_("Number of ports."))
stack = models.ForeignKey(
@ -269,8 +287,9 @@ class Switch(Machine):
verbose_name_plural = _("switches")
def clean(self):
""" Verifie que l'id stack est dans le bon range
Appelle également le clean de la classe parente"""
"""Check if the stack member ID is in the range of the stack's IDs and
calls the clean of the parent class.
"""
super(Switch, self).clean()
if self.stack is not None:
if self.stack_member_id is not None:
@ -291,8 +310,12 @@ class Switch(Machine):
)
def create_ports(self, begin, end):
""" Crée les ports de begin à end si les valeurs données
sont cohérentes. """
"""Create ports for the switch if the values are consistent.
Args:
begin: the number of the start port.
end: the number of the end port.
"""
if end < begin:
raise ValidationError(_("The end port is less than the start port."))
ports_to_create = range(begin, end + 1)
@ -313,8 +336,7 @@ class Switch(Machine):
)
def main_interface(self):
""" Returns the 'main' interface of the switch
It must the the management interface for that device"""
"""Get the main interface of the switch (the management interface)."""
switch_iptype = OptionalTopologie.get_cached_value("switchs_ip_type")
if switch_iptype:
return (
@ -329,12 +351,14 @@ class Switch(Machine):
@cached_property
def get_radius_key(self):
"""Retourne l'objet de la clef radius de ce switch"""
"""Get the RADIUS key object related to the switch."""
return self.radius_key or RadiusKey.objects.filter(default_switch=True).first()
@cached_property
def get_radius_key_value(self):
"""Retourne la valeur en str de la clef radius, none si il n'y en a pas"""
"""Get the RADIUS key as a string, or None if there are no RADIUS key
related to the switch.
"""
if self.get_radius_key:
return self.get_radius_key.radius_key
else:
@ -362,7 +386,7 @@ class Switch(Machine):
@cached_property
def get_management_cred(self):
"""Retourne l'objet des creds de managament de ce switch"""
"""Get the management credentials objects of the switch."""
return (
self.management_creds
or SwitchManagementCred.objects.filter(default_switch=True).first()
@ -370,7 +394,9 @@ class Switch(Machine):
@cached_property
def get_management_cred_value(self):
"""Retourne un dict des creds de management du switch"""
"""Get the management credentials as a dictionary, or None if there are
no management credentials related to the switch.
"""
if self.get_management_cred:
return {
"id": self.get_management_cred.management_id,
@ -401,17 +427,19 @@ class Switch(Machine):
@cached_property
def ipv4(self):
"""Return the switch's management ipv4"""
"""Get the IPv4 address of the switch's management interface."""
return str(self.main_interface().ipv4)
@cached_property
def ipv6(self):
"""Returne the switch's management ipv6"""
"""Get the IPv6 address of the switch's management interface."""
return str(self.main_interface().ipv6().first())
@cached_property
def interfaces_subnet(self):
"""Return dict ip:subnet for all ip of the switch"""
"""Get a dictionary of IPv4 addresses:subnets of all the switch's
interfaces.
"""
return dict(
(
str(interface.ipv4),
@ -423,7 +451,9 @@ class Switch(Machine):
@cached_property
def interfaces6_subnet(self):
"""Return dict ip6:subnet for all ipv6 of the switch"""
"""Get a dictionary of IPv6 addresses:subnets of all the switch's
interfaces.
"""
return dict(
(
str(interface.ipv6().first()),
@ -434,7 +464,9 @@ class Switch(Machine):
@cached_property
def list_modules(self):
"""Return modules of that switch, list of dict (rank, reference)"""
"""Get the list of dictionaries (rank, reference) of modules related to
the switch.
"""
modules = []
if getattr(self.model, "is_modular", None):
if self.model.is_itself_module:
@ -445,7 +477,7 @@ class Switch(Machine):
@cached_property
def get_dormitory(self):
"""Returns the dormitory of that switch"""
"""Get the dormitory in which the switch is located."""
if self.switchbay:
return self.switchbay.building.dormitory
else:
@ -453,19 +485,19 @@ class Switch(Machine):
@classmethod
def nothing_profile(cls):
"""Return default nothing port profile"""
"""Return default nothing port profile."""
nothing_profile, _created = PortProfile.objects.get_or_create(
profil_default="nothing", name="nothing", radius_type="NO"
)
return nothing_profile
def profile_type_or_nothing(self, profile_type):
"""Return the profile for a profile_type of this switch
"""Return the profile for a profile_type of this switch.
If exists, returns the defined default profile for a profile type on the dormitory which
the switch belongs
Otherwise, returns the nothing profile"""
If it exists, return the defined default profile for a profile type on
the dormitory which the switch belongs.
Otherwise, return the nothing profile.
"""
profile_queryset = PortProfile.objects.filter(profil_default=profile_type)
if self.get_dormitory:
port_profile = (
@ -478,22 +510,22 @@ class Switch(Machine):
@cached_property
def default_uplink_profile(self):
"""Default uplink profile for that switch -- in cache"""
"""Default uplink profile for that switch -- in cache."""
return self.profile_type_or_nothing("uplink")
@cached_property
def default_access_point_profile(self):
"""Default ap profile for that switch -- in cache"""
"""Default AP profile for that switch -- in cache."""
return self.profile_type_or_nothing("access_point")
@cached_property
def default_room_profile(self):
"""Default room profile for that switch -- in cache"""
"""Default room profile for that switch -- in cache."""
return self.profile_type_or_nothing("room")
@cached_property
def default_asso_machine_profile(self):
"""Default asso machine profile for that switch -- in cache"""
"""Default asso machine profile for that switch -- in cache."""
return self.profile_type_or_nothing("asso_machine")
def __str__(self):
@ -522,7 +554,16 @@ class Switch(Machine):
class ModelSwitch(AclMixin, RevMixin, models.Model):
"""Un modèle (au sens constructeur) de switch"""
"""Switch model.
Attributes:
reference: the reference of the switch model.
commercial_name: the commercial name of the switch model.
constructor: the constructor of the switch model.
firmware: the firmware of the switch model.
is_modular: whether the switch model is modular.
is_itself_module: whether the switch is considered as a module.
"""
reference = models.CharField(max_length=255)
commercial_name = models.CharField(max_length=255, null=True, blank=True)
@ -550,7 +591,12 @@ class ModelSwitch(AclMixin, RevMixin, models.Model):
class ModuleSwitch(AclMixin, RevMixin, models.Model):
"""A module of a switch"""
"""Switch module.
Attributes:
reference: the reference of the switch module.
comment: the comment to describe the switch module.
"""
reference = models.CharField(
max_length=255,
@ -575,7 +621,13 @@ class ModuleSwitch(AclMixin, RevMixin, models.Model):
class ModuleOnSwitch(AclMixin, RevMixin, models.Model):
"""Link beetween module and switch"""
"""Link beetween module and switch.
Attributes:
module: the switch module related to the link.
switch: the switch related to the link.
slot: the slot on the switch related to the link.
"""
module = models.ForeignKey("ModuleSwitch", on_delete=models.CASCADE)
switch = models.ForeignKey("Switch", on_delete=models.CASCADE)
@ -601,7 +653,11 @@ class ModuleOnSwitch(AclMixin, RevMixin, models.Model):
class ConstructorSwitch(AclMixin, RevMixin, models.Model):
"""Un constructeur de switch"""
"""Switch constructor.
Attributes:
name: the name of the switch constructor.
"""
name = models.CharField(max_length=255)
@ -617,7 +673,13 @@ class ConstructorSwitch(AclMixin, RevMixin, models.Model):
class SwitchBay(AclMixin, RevMixin, models.Model):
"""Une baie de brassage"""
"""Switch bay.
Attributes:
name: the name of the switch bay.
building: the building in which the switch bay is located.
info: the information to describe to switch bay.
"""
name = models.CharField(max_length=255)
building = models.ForeignKey("Building", on_delete=models.PROTECT)
@ -633,8 +695,11 @@ class SwitchBay(AclMixin, RevMixin, models.Model):
class Dormitory(AclMixin, RevMixin, models.Model):
"""A student accomodation/dormitory
Une résidence universitaire"""
"""Dormitory.
Attributes:
name: the name of the dormitory.
"""
name = models.CharField(max_length=255)
@ -644,7 +709,7 @@ class Dormitory(AclMixin, RevMixin, models.Model):
verbose_name_plural = _("dormitories")
def all_ap_in(self):
"""Returns all ap of the dorms"""
"""Get all the APs in the dormitory."""
return AccessPoint.all_ap_in(self.building_set.all())
@classmethod
@ -660,8 +725,13 @@ class Dormitory(AclMixin, RevMixin, models.Model):
class Building(AclMixin, RevMixin, models.Model):
"""A building of a dormitory
Un batiment"""
"""Building.
Attributes:
name: the name of the building.
dormitory: the dormitory of the building (a Dormitory can contain
multiple dormitories).
"""
name = models.CharField(max_length=255)
dormitory = models.ForeignKey("Dormitory", on_delete=models.PROTECT)
@ -672,7 +742,7 @@ class Building(AclMixin, RevMixin, models.Model):
verbose_name_plural = _("buildings")
def all_ap_in(self):
"""Returns all ap of the building"""
"""Get all the APs in the building."""
return AccessPoint.all_ap_in(self)
def get_name(self):
@ -690,21 +760,32 @@ class Building(AclMixin, RevMixin, models.Model):
class Port(AclMixin, RevMixin, models.Model):
""" Definition d'un port. Relié à un switch(foreign_key),
un port peut etre relié de manière exclusive à :
- une chambre (room)
- une machine (serveur etc) (machine_interface)
- un autre port (uplink) (related)
Champs supplémentaires :
- RADIUS (mode STRICT : connexion sur port uniquement si machine
d'un adhérent à jour de cotisation et que la chambre est également à
jour de cotisation
mode COMMON : vérification uniquement du statut de la machine
mode NO : accepte toute demande venant du port et place sur le vlan normal
mode BLOQ : rejet de toute authentification
- vlan_force : override la politique générale de placement vlan, permet
de forcer un port sur un vlan particulier. S'additionne à la politique
RADIUS"""
"""Port of a switch.
A port is related exclusively to either:
* a room
* a machine, e.g. server
* another port
Behaviour according to the RADIUS mode:
* STRICT: connection only if the machine and room have access
* COMMON: check only the machine's state
* NO: accept only request coming from the port and set on the standard
VLAN.
* BLOQ: reject all requests.
The VLAN can be forced to override the general policy for VLAN setting.
This enables to force a port to a particular VLAN. It adds to the RADIUS
policy.
Attributes:
switch: the switch to which the port belongs.
port: the port number on the switch for the Port object.
room: the room to which the port is related.
machine_interface: the machine to which the port is related
related: the other port to which is port is related.
custom_profile: the port profile of the port.
state: whether the port is active.
details: the details to describre the port.
"""
switch = models.ForeignKey("Switch", related_name="ports", on_delete=models.CASCADE)
port = models.PositiveIntegerField()
@ -733,7 +814,7 @@ class Port(AclMixin, RevMixin, models.Model):
@cached_property
def pretty_name(self):
"""More elaborated name for label on switch conf"""
"""More elaborated name for label on switch configuration."""
if self.related:
return _("Uplink: ") + self.related.switch.short_name
elif self.machine_interface:
@ -745,14 +826,13 @@ class Port(AclMixin, RevMixin, models.Model):
@cached_property
def get_port_profile(self):
"""Return the config profil for this port
:returns: the profile of self (port)
If is defined a custom profile, returns it
elIf a default profile is defined for its dormitory, returns it
Else, returns the global default profil
If not exists, create a nothing profile"""
"""Get the configuration profile for this port.
Returns:
The custom profile if it exists, else the default profile of the
dormitory if it exists, else the global default profile, else the
nothing profile.
"""
if self.custom_profile:
return self.custom_profile
elif self.related:
@ -779,26 +859,25 @@ class Port(AclMixin, RevMixin, models.Model):
)
def make_port_related(self):
""" Synchronise le port distant sur self"""
"""Synchronise the related port with self."""
related_port = self.related
related_port.related = self
related_port.save()
def clean_port_related(self):
""" Supprime la relation related sur self"""
"""Delete the related relation on self."""
related_port = self.related_port
related_port.related = None
related_port.save()
def clean(self):
""" Verifie que un seul de chambre, interface_parent et related_port
est rempli. Verifie que le related n'est pas le port lui-même....
Verifie que le related n'est pas déjà occupé par une machine ou une
chambre. Si ce n'est pas le cas, applique la relation related
Si un port related point vers self, on nettoie la relation
A priori pas d'autre solution que de faire ça à la main. A priori
tout cela est dans un bloc transaction, donc pas de problème de
cohérence"""
"""
Check if the port is only related exclusively to either a room, a
machine or another port.
Check if the related port is not self and applies the relation to the
related port if the relation is correct.
Delete the relation if it points to self.
"""
if hasattr(self, "switch"):
if self.port > self.switch.number:
raise ValidationError(
@ -835,7 +914,13 @@ class Port(AclMixin, RevMixin, models.Model):
class Room(AclMixin, RevMixin, models.Model):
"""Une chambre/local contenant une prise murale"""
"""Room.
Attributes:
name: the name of the room.
details: the details describing the room.
building: the building in which the room is located.
"""
name = models.CharField(max_length=255)
details = models.CharField(max_length=255, blank=True)
@ -853,7 +938,28 @@ class Room(AclMixin, RevMixin, models.Model):
class PortProfile(AclMixin, RevMixin, models.Model):
"""Contains the information of the ports' configuration for a switch"""
"""Port profile.
Contains the information of the ports' configuration for a switch.
Attributes:
name: the name of the port profile.
profil_default: the type of default profile (room, AP, uplink etc.).
on_dormitory: the dormitory with this default port profile.
vlan_untagged: the VLAN untagged of the port profile.
vlan_tagged: the VLAN(s) tagged of the port profile.
radius_type: the type of RADIUS authentication (inactive, MAC-address
or 802.1X) of the port profile.
radius_mode: the RADIUS mode of the port profile.
speed: the port speed limit of the port profile.
mac_limit: the MAC limit of the port profile.
flow_control: whether flow control is enabled.
dhcp_snooping: whether DHCP snooping is enabled.
dhcpv6_snooping: whether DHCPv6 snooping is enabled.
arp_protect: whether ARP protection is enabled.
ra_guard: whether RA guard is enabled.
loop_protect: whether loop protection is enabled.
"""
TYPES = (("NO", "NO"), ("802.1X", "802.1X"), ("MAC-radius", _("MAC-RADIUS")))
MODES = (("STRICT", "STRICT"), ("COMMON", "COMMON"))
@ -983,7 +1089,7 @@ class PortProfile(AclMixin, RevMixin, models.Model):
return ",".join(self.security_parameters_enabled)
def clean(self):
""" Check that there is only one generic profil default"""
"""Check that there is only one generic profile default."""
super(PortProfile, self).clean()
if (
self.profil_default
@ -1007,21 +1113,21 @@ class PortProfile(AclMixin, RevMixin, models.Model):
@receiver(post_save, sender=AccessPoint)
def ap_post_save(**_kwargs):
"""Regeneration des noms des bornes vers le controleur"""
"""Regenerate the AP names towards the controller."""
regen("unifi-ap-names")
regen("graph_topo")
@receiver(post_delete, sender=AccessPoint)
def ap_post_delete(**_kwargs):
"""Regeneration des noms des bornes vers le controleur"""
"""Regenerate the AP names towards the controller."""
regen("unifi-ap-names")
regen("graph_topo")
@receiver(post_delete, sender=Stack)
def stack_post_delete(**_kwargs):
"""Vide les id des switches membres d'une stack supprimée"""
"""Empty the stack member ID of switches when a stack is deleted."""
Switch.objects.filter(stack=None).update(stack_member_id=None)

View file

@ -19,11 +19,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.
"""
Definition des urls de l'application topologie.
Inclu dans urls de re2o.
Fait référence aux fonctions du views
"""topologie.urls
The defined URLs for topologie app. Included in re2o.urls.
"""
from __future__ import unicode_literals

View file

@ -20,18 +20,17 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Page des vues de l'application topologie
Views for the 'topologie' app of re2o.
Permet de créer, modifier et supprimer :
- un port (add_port, edit_port, del_port)
- un switch : les vues d'ajout et d'édition font appel aux forms de creation
de switch, mais aussi aux forms de machines.forms (domain, interface et
machine). Le views les envoie et les save en même temps. TODO : rationaliser
et faire que la creation de machines (interfaces, domain etc) soit gérée
coté models et forms de topologie
- une chambre (new_room, edit_room, del_room)
- une stack
- l'historique de tous les objets cités
They are used to create, edit and delete:
* a port (add_port, edit_port, del_port)
* a switch: the views call forms for switches but also machines (domain,
interface and machine), send and save them at the same time.
TODO rationalise, enforce the creation of machines (interfaces, domains
etc.) in models and forms from 'topologie'
* a room (new_room, edit_room, del_room)
* a stack
* histories of all objects mentioned.
"""
from __future__ import unicode_literals
@ -105,7 +104,7 @@ from os.path import isfile
@login_required
@can_view_all(Switch)
def index(request):
""" Vue d'affichage de tous les swicthes"""
"""View used to display all switches."""
switch_list = (
Switch.objects.prefetch_related(
Prefetch(
@ -171,7 +170,7 @@ def index_port_profile(request):
@can_view_all(Port)
@can_view(Switch)
def index_port(request, switch, switchid):
""" Affichage de l'ensemble des ports reliés à un switch particulier"""
"""View used to display all ports related to the given switch."""
port_list = (
Port.objects.filter(switch=switch)
.select_related("room__building__dormitory")
@ -204,7 +203,7 @@ def index_port(request, switch, switchid):
@login_required
@can_view_all(Room)
def index_room(request):
""" Affichage de l'ensemble des chambres"""
"""View used to display all rooms."""
room_list = Room.objects.select_related("building__dormitory")
room_list = SortTable.sort(
room_list,
@ -220,7 +219,7 @@ def index_room(request):
@login_required
@can_view_all(AccessPoint)
def index_ap(request):
""" Affichage de l'ensemble des bornes"""
"""View used to display all APs."""
ap_list = AccessPoint.objects.prefetch_related(
Prefetch(
"interface_set",
@ -245,7 +244,7 @@ def index_ap(request):
@login_required
@can_view_all(Stack, Building, Dormitory, SwitchBay)
def index_physical_grouping(request):
"""Affichage de la liste des stacks (affiche l'ensemble des switches)"""
"""View used to display the list of stacks (display all switches)."""
stack_list = Stack.objects.prefetch_related(
"switch_set__interface_set__domain__extension"
)
@ -293,7 +292,7 @@ def index_physical_grouping(request):
@login_required
@can_view_all(ModelSwitch, ConstructorSwitch)
def index_model_switch(request):
""" Affichage de l'ensemble des modèles de switches"""
"""View used to display all switch models."""
model_switch_list = ModelSwitch.objects.select_related(
"constructor"
).prefetch_related("switch_set__interface_set__domain")
@ -323,7 +322,7 @@ def index_model_switch(request):
@login_required
@can_view_all(ModuleSwitch)
def index_module(request):
"""Display all modules of switchs"""
"""View used to display all switch modules."""
module_list = ModuleSwitch.objects.all()
modular_switchs = (
Switch.objects.filter(model__is_modular=True)
@ -342,7 +341,7 @@ def index_module(request):
@login_required
@can_edit(Vlan)
def edit_vlanoptions(request, vlan_instance, **_kwargs):
""" View used to edit options for switch of VLAN object """
"""View used to edit options for switch of VLAN object."""
vlan = EditOptionVlanForm(request.POST or None, instance=vlan_instance)
if vlan.is_valid():
if vlan.changed_data:
@ -357,7 +356,7 @@ def edit_vlanoptions(request, vlan_instance, **_kwargs):
@login_required
@can_create(Port)
def new_port(request, switchid):
""" Nouveau port"""
"""View used to create ports."""
try:
switch = Switch.objects.get(pk=switchid)
except Switch.DoesNotExist:
@ -383,9 +382,10 @@ def new_port(request, switchid):
@login_required
@can_edit(Port)
def edit_port(request, port_object, **_kwargs):
""" Edition d'un port. Permet de changer le switch parent et
l'affectation du port"""
"""View used to edit ports.
It enables to change the related switch and the port assignment.
"""
port = EditPortForm(request.POST or None, instance=port_object)
if port.is_valid():
if port.changed_data:
@ -410,7 +410,7 @@ def edit_port(request, port_object, **_kwargs):
@login_required
@can_delete(Port)
def del_port(request, port, **_kwargs):
""" Supprime le port"""
"""View used to delete ports."""
if request.method == "POST":
try:
port.delete()
@ -435,7 +435,7 @@ def del_port(request, port, **_kwargs):
@login_required
@can_create(Stack)
def new_stack(request):
"""Ajoute un nouveau stack : stackid_min, max, et nombre de switches"""
"""View used to create stacks."""
stack = StackForm(request.POST or None)
if stack.is_valid():
stack.save()
@ -449,7 +449,7 @@ def new_stack(request):
@login_required
@can_edit(Stack)
def edit_stack(request, stack, **_kwargs):
"""Edition d'un stack (nombre de switches, nom...)"""
"""View used to edit stacks."""
stack = StackForm(request.POST or None, instance=stack)
if stack.is_valid():
if stack.changed_data:
@ -464,7 +464,7 @@ def edit_stack(request, stack, **_kwargs):
@login_required
@can_delete(Stack)
def del_stack(request, stack, **_kwargs):
"""Supprime un stack"""
"""View used to delete stacks."""
if request.method == "POST":
try:
stack.delete()
@ -487,8 +487,7 @@ def del_stack(request, stack, **_kwargs):
@login_required
@can_edit(Stack)
def edit_switchs_stack(request, stack, **_kwargs):
"""Permet d'éditer la liste des switches dans une stack et l'ajouter"""
"""View used to edit the list of switches of the given stack."""
if request.method == "POST":
pass
else:
@ -500,9 +499,12 @@ def edit_switchs_stack(request, stack, **_kwargs):
@login_required
@can_create(Switch)
def new_switch(request):
""" Creation d'un switch. Cree en meme temps l'interface et la machine
associée. Vue complexe. Appelle successivement les 4 models forms
adaptés : machine, interface, domain et switch"""
"""View used to create switches.
At the same time, it creates the related interface and machine. The view
successively calls the 4 appropriate forms: machine, interface, domain and
switch.
"""
switch = NewSwitchForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(request.POST or None, user=request.user)
domain = DomainForm(request.POST or None, user=request.user)
@ -549,7 +551,7 @@ def new_switch(request):
@login_required
@can_create(Port)
def create_ports(request, switchid):
""" Création d'une liste de ports pour un switch."""
"""View used to create port lists for the given switch."""
try:
switch = Switch.objects.get(pk=switchid)
except Switch.DoesNotExist:
@ -578,9 +580,11 @@ def create_ports(request, switchid):
@login_required
@can_edit(Switch)
def edit_switch(request, switch, switchid):
""" Edition d'un switch. Permet de chambre nombre de ports,
place dans le stack, interface et machine associée"""
"""View used to edit switches.
It enables to change the number of ports, location in the stack, or the
related interface and machine.
"""
switch_form = EditSwitchForm(
request.POST or None, instance=switch, user=request.user
)
@ -622,9 +626,11 @@ def edit_switch(request, switch, switchid):
@login_required
@can_create(AccessPoint)
def new_ap(request):
""" Creation d'une ap. Cree en meme temps l'interface et la machine
associée. Vue complexe. Appelle successivement les 3 models forms
adaptés : machine, interface, domain et switch"""
"""View used to create APs.
At the same time, it creates the related interface and machine. The view
successively calls the 3 appropriate forms: machine, interface, domain.
"""
ap = AddAccessPointForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(request.POST or None, user=request.user)
domain = DomainForm(request.POST or None, user=request.user)
@ -671,8 +677,7 @@ def new_ap(request):
@login_required
@can_edit(AccessPoint)
def edit_ap(request, ap, **_kwargs):
""" Edition d'un switch. Permet de chambre nombre de ports,
place dans le stack, interface et machine associée"""
"""View used to edit APs."""
interface_form = EditInterfaceForm(
request.POST or None, user=request.user, instance=ap.interface_set.first()
)
@ -723,7 +728,7 @@ def edit_ap(request, ap, **_kwargs):
@login_required
@can_create(Room)
def new_room(request):
"""Nouvelle chambre """
"""View used to create rooms."""
room = EditRoomForm(request.POST or None)
if room.is_valid():
room.save()
@ -737,7 +742,7 @@ def new_room(request):
@login_required
@can_edit(Room)
def edit_room(request, room, **_kwargs):
""" Edition numero et details de la chambre"""
"""View used to edit rooms."""
room = EditRoomForm(request.POST or None, instance=room)
if room.is_valid():
if room.changed_data:
@ -752,7 +757,7 @@ def edit_room(request, room, **_kwargs):
@login_required
@can_delete(Room)
def del_room(request, room, **_kwargs):
""" Suppression d'un chambre"""
"""View used to delete rooms."""
if request.method == "POST":
try:
room.delete()
@ -777,7 +782,7 @@ def del_room(request, room, **_kwargs):
@login_required
@can_create(ModelSwitch)
def new_model_switch(request):
"""Nouveau modèle de switch"""
"""View used to create switch models."""
model_switch = EditModelSwitchForm(request.POST or None)
if model_switch.is_valid():
model_switch.save()
@ -793,8 +798,7 @@ def new_model_switch(request):
@login_required
@can_edit(ModelSwitch)
def edit_model_switch(request, model_switch, **_kwargs):
""" Edition d'un modèle de switch"""
"""View used to edit switch models."""
model_switch = EditModelSwitchForm(request.POST or None, instance=model_switch)
if model_switch.is_valid():
if model_switch.changed_data:
@ -811,7 +815,7 @@ def edit_model_switch(request, model_switch, **_kwargs):
@login_required
@can_delete(ModelSwitch)
def del_model_switch(request, model_switch, **_kwargs):
""" Suppression d'un modèle de switch"""
"""View used to delete switch models."""
if request.method == "POST":
try:
model_switch.delete()
@ -838,7 +842,7 @@ def del_model_switch(request, model_switch, **_kwargs):
@login_required
@can_create(SwitchBay)
def new_switch_bay(request):
"""Nouvelle baie de switch"""
"""View used to create switch bays."""
switch_bay = EditSwitchBayForm(request.POST or None)
if switch_bay.is_valid():
switch_bay.save()
@ -854,7 +858,7 @@ def new_switch_bay(request):
@login_required
@can_edit(SwitchBay)
def edit_switch_bay(request, switch_bay, **_kwargs):
""" Edition d'une baie de switch"""
"""View used to edit switch bays."""
switch_bay = EditSwitchBayForm(request.POST or None, instance=switch_bay)
if switch_bay.is_valid():
if switch_bay.changed_data:
@ -871,7 +875,7 @@ def edit_switch_bay(request, switch_bay, **_kwargs):
@login_required
@can_delete(SwitchBay)
def del_switch_bay(request, switch_bay, **_kwargs):
""" Suppression d'une baie de switch"""
"""View used to delete switch bays."""
if request.method == "POST":
try:
switch_bay.delete()
@ -898,8 +902,7 @@ def del_switch_bay(request, switch_bay, **_kwargs):
@login_required
@can_create(Building)
def new_building(request):
"""New Building of a dorm
Nouveau batiment"""
"""View used to create buildings."""
building = EditBuildingForm(request.POST or None)
if building.is_valid():
building.save()
@ -915,8 +918,7 @@ def new_building(request):
@login_required
@can_edit(Building)
def edit_building(request, building, **_kwargs):
"""Edit a building
Edition d'un batiment"""
"""View used to edit buildings."""
building = EditBuildingForm(request.POST or None, instance=building)
if building.is_valid():
if building.changed_data:
@ -931,8 +933,7 @@ def edit_building(request, building, **_kwargs):
@login_required
@can_delete(Building)
def del_building(request, building, **_kwargs):
"""Delete a building
Suppression d'un batiment"""
"""View used to delete buildings."""
if request.method == "POST":
try:
building.delete()
@ -959,8 +960,7 @@ def del_building(request, building, **_kwargs):
@login_required
@can_create(Dormitory)
def new_dormitory(request):
"""A new dormitory
Nouvelle residence"""
"""View used to create dormitories."""
dormitory = EditDormitoryForm(request.POST or None)
if dormitory.is_valid():
dormitory.save()
@ -976,8 +976,7 @@ def new_dormitory(request):
@login_required
@can_edit(Dormitory)
def edit_dormitory(request, dormitory, **_kwargs):
"""Edit a dormitory
Edition d'une residence"""
"""View used to edit dormitories."""
dormitory = EditDormitoryForm(request.POST or None, instance=dormitory)
if dormitory.is_valid():
if dormitory.changed_data:
@ -994,8 +993,7 @@ def edit_dormitory(request, dormitory, **_kwargs):
@login_required
@can_delete(Dormitory)
def del_dormitory(request, dormitory, **_kwargs):
"""Delete a dormitory
Suppression d'une residence"""
"""View used to delete dormitories."""
if request.method == "POST":
try:
dormitory.delete()
@ -1022,7 +1020,7 @@ def del_dormitory(request, dormitory, **_kwargs):
@login_required
@can_create(ConstructorSwitch)
def new_constructor_switch(request):
"""Nouveau constructeur de switch"""
"""View used to create switch constructors."""
constructor_switch = EditConstructorSwitchForm(request.POST or None)
if constructor_switch.is_valid():
constructor_switch.save()
@ -1038,8 +1036,7 @@ def new_constructor_switch(request):
@login_required
@can_edit(ConstructorSwitch)
def edit_constructor_switch(request, constructor_switch, **_kwargs):
""" Edition d'un constructeur de switch"""
"""View used to edit switch constructors."""
constructor_switch = EditConstructorSwitchForm(
request.POST or None, instance=constructor_switch
)
@ -1058,7 +1055,7 @@ def edit_constructor_switch(request, constructor_switch, **_kwargs):
@login_required
@can_delete(ConstructorSwitch)
def del_constructor_switch(request, constructor_switch, **_kwargs):
""" Suppression d'un constructeur de switch"""
"""View used to delete switch constructors."""
if request.method == "POST":
try:
constructor_switch.delete()
@ -1085,7 +1082,7 @@ def del_constructor_switch(request, constructor_switch, **_kwargs):
@login_required
@can_create(PortProfile)
def new_port_profile(request):
"""Create a new port profile"""
"""View used to create port profiles."""
port_profile = EditPortProfileForm(request.POST or None)
if port_profile.is_valid():
port_profile.save()
@ -1101,7 +1098,7 @@ def new_port_profile(request):
@login_required
@can_edit(PortProfile)
def edit_port_profile(request, port_profile, **_kwargs):
"""Edit a port profile"""
"""View used to edit port profiles."""
port_profile = EditPortProfileForm(request.POST or None, instance=port_profile)
if port_profile.is_valid():
if port_profile.changed_data:
@ -1118,7 +1115,7 @@ def edit_port_profile(request, port_profile, **_kwargs):
@login_required
@can_delete(PortProfile)
def del_port_profile(request, port_profile, **_kwargs):
"""Delete a port profile"""
"""View used to delete port profiles."""
if request.method == "POST":
try:
port_profile.delete()
@ -1136,7 +1133,7 @@ def del_port_profile(request, port_profile, **_kwargs):
@login_required
@can_create(ModuleSwitch)
def add_module(request):
""" View used to add a Module object """
"""View used to create switch modules."""
module = EditModuleForm(request.POST or None)
if module.is_valid():
module.save()
@ -1150,7 +1147,7 @@ def add_module(request):
@login_required
@can_edit(ModuleSwitch)
def edit_module(request, module_instance, **_kwargs):
""" View used to edit a Module object """
"""View used to edit switch modules."""
module = EditModuleForm(request.POST or None, instance=module_instance)
if module.is_valid():
if module.changed_data:
@ -1165,7 +1162,7 @@ def edit_module(request, module_instance, **_kwargs):
@login_required
@can_delete(ModuleSwitch)
def del_module(request, module, **_kwargs):
"""Compleete delete a module"""
"""View used to delete switch modules."""
if request.method == "POST":
try:
module.delete()
@ -1190,7 +1187,7 @@ def del_module(request, module, **_kwargs):
@login_required
@can_create(ModuleOnSwitch)
def add_module_on(request):
"""Add a module to a switch"""
"""View used to add a module to a switch."""
module_switch = EditSwitchModuleForm(request.POST or None)
if module_switch.is_valid():
module_switch.save()
@ -1206,7 +1203,7 @@ def add_module_on(request):
@login_required
@can_edit(ModuleOnSwitch)
def edit_module_on(request, module_instance, **_kwargs):
""" View used to edit a Module object """
"""View used to edit a module on a switch."""
module = EditSwitchModuleForm(request.POST or None, instance=module_instance)
if module.is_valid():
if module.changed_data:
@ -1221,7 +1218,7 @@ def edit_module_on(request, module_instance, **_kwargs):
@login_required
@can_delete(ModuleOnSwitch)
def del_module_on(request, module, **_kwargs):
"""Compleete delete a module"""
"""View used to delete a module on a switch."""
if request.method == "POST":
try:
module.delete()
@ -1244,9 +1241,7 @@ def del_module_on(request, module, **_kwargs):
def make_machine_graph():
"""
Create the graph of switchs, machines and access points.
"""
"""Create the graph of switches, machines and access points."""
dico = {
"subs": [],
"links": [],
@ -1284,7 +1279,7 @@ def make_machine_graph():
"machines": [],
}
)
# Visit all switchs in this building
# Visit all switches in this building
for switch in (
Switch.objects.filter(switchbay__building=building)
.prefetch_related(
@ -1311,7 +1306,7 @@ def make_machine_graph():
"ports": [],
}
)
# visit all ports of this switch and add the switchs linked to it
# visit all ports of this switch and add the switches linked to it
for port in switch.ports.filter(related__isnull=False).select_related(
"related__switch"
):
@ -1365,29 +1360,29 @@ def make_machine_graph():
links, new_detected = recursive_switchs(missing[0], None, [missing[0]])
for link in links:
dico["links"].append(link)
# Update the lists of missings and already detected switchs
# Update the lists of missings and already detected switches
missing = [i for i in missing if i not in new_detected]
detected += new_detected
# If the switch have no ports, don't explore it and hop to the next one
# If the switch has no ports, don't explore it and hop to the next one
else:
del missing[0]
# Switchs that are not connected or not in a building
# Switches that are not connected or not in a building
for switch in Switch.objects.filter(switchbay__isnull=True).exclude(
ports__related__isnull=False
):
dico["alone"].append({"id": switch.id, "name": switch.get_name})
# generate the dot file
# Generate the dot file
dot_data = generate_dot(dico, "topologie/graph_switch.dot")
# Create a temporary file to store the dot data
f = tempfile.NamedTemporaryFile(mode="w+", encoding="utf-8", delete=False)
with f:
f.write(dot_data)
unflatten = Popen( # unflatten the graph to make it look better
unflatten = Popen( # Unflatten the graph to make it look better
["unflatten", "-l", "3", f.name], stdout=PIPE
)
Popen( # pipe the result of the first command into the second
Popen( # Pipe the result of the first command into the second
["dot", "-Tpng", "-o", MEDIA_ROOT + "/images/switchs.png"],
stdin=unflatten.stdout,
stdout=PIPE,
@ -1395,10 +1390,15 @@ def make_machine_graph():
def generate_dot(data, template):
"""create the dot file
:param data: dictionary passed to the template
:param template: path to the dot template
:return: all the lines of the dot file"""
"""Generate a dot file from the data and template given.
Args:
data: dictionary passed to the template.
template: path to the dot template.
Returns:
All the lines of the dot file.
"""
t = loader.get_template(template)
if not isinstance(t, Template) and not (
hasattr(t, "template") and isinstance(t.template, Template)
@ -1415,18 +1415,19 @@ def generate_dot(data, template):
def recursive_switchs(switch_start, switch_before, detected):
"""Visit the switch and travel to the switchs linked to it.
:param switch_start: the switch to begin the visit on
:param switch_before: the switch that you come from.
None if switch_start is the first one
:param detected: list of all switchs already visited.
None if switch_start is the first one
:return: A list of all the links found and a list of
all the switchs visited
"""Visit the switch and travel to the switches linked to it.
Args:
switch_start: the switch to begin the visit on.
switch_before: the switch that you come from. None if switch_start is the first one.
detected: list of all switches already visited. None if switch_start is the first one.
Returns:
A list of all the links found and a list of all the switches visited.
"""
detected.append(switch_start)
links_return = [] # list of dictionaries of the links to be detected
# create links to every switchs below
links_return = [] # List of dictionaries of the links to be detected
# Create links to every switches below
for port in switch_start.ports.filter(related__isnull=False):
# Not the switch that we come from, not the current switch
if (
@ -1440,11 +1441,11 @@ def recursive_switchs(switch_start, switch_before, detected):
}
links_return.append(links) # Add current and below levels links
# go down on every related switchs
# Go down on every related switches
for port in switch_start.ports.filter(related__isnull=False):
# The switch at the end of this link has not been visited
if port.related.switch not in detected:
# explore it and get the results
# Explore it and get the results
links_down, detected = recursive_switchs(
port.related.switch, switch_start, detected
)

View file

@ -21,8 +21,10 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Definition des vues pour les admin. Classique, sauf pour users,
on fait appel à UserChange et ServiceUserChange, forms custom
Admin views basic definition, include basic definition of admin view.
Except for Admin edition and creation of users and services users;
with AdherentAdmin, ClubAdmin and ServiceUserAdmin.
"""
from __future__ import unicode_literals
@ -56,15 +58,26 @@ from .forms import (
class LdapUserAdmin(admin.ModelAdmin):
"""Administration du ldapuser"""
"""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):
"""Administration du ldapserviceuser"""
"""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",)
@ -72,63 +85,123 @@ class LdapServiceUserAdmin(admin.ModelAdmin):
class LdapUserGroupAdmin(admin.ModelAdmin):
"""Administration du ldapusergroupe"""
"""LdapUserGroup Admin view.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("name", "members", "gid")
search_fields = ("name",)
class LdapServiceUserGroupAdmin(admin.ModelAdmin):
"""Administration du ldap serviceusergroup"""
"""LdapServiceUserGroup Admin view.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("name",)
search_fields = ("name",)
class SchoolAdmin(VersionAdmin):
"""Administration, gestion des écoles"""
"""School Admin view and management.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass
class ListRightAdmin(VersionAdmin):
"""Gestion de la liste des droits existants
Ne permet pas l'edition du gid (primarykey pour ldap)"""
"""ListRight and groups Admin view and management.
Even if it is possible, gid should NOT be changed
as it is the ldap primary key.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("unix_name",)
class ListShellAdmin(VersionAdmin):
"""Gestion de la liste des shells coté admin"""
"""Users Shell Admin view and management.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass
class RequestAdmin(admin.ModelAdmin):
"""Gestion des request objet, ticket pour lien de reinit mot de passe"""
"""User Request Admin view and management, for
change password and email validation.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("user", "type", "created_at", "expires_at")
class BanAdmin(VersionAdmin):
"""Gestion des bannissements"""
"""Ban Admin view and management, for
User Ban
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass
class EMailAddressAdmin(VersionAdmin):
"""Gestion des alias mail"""
"""EmailAddress Admin view and management, for
auxiliary and local email addresses
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass
class WhitelistAdmin(VersionAdmin):
"""Gestion des whitelist"""
"""Whitelist Admin view and management, for
free access whitelisted users
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass
class AdherentAdmin(VersionAdmin, BaseUserAdmin):
"""Adherent Admin view and management, for
Adherent fields : password, pseudo, etc, admin can
edit all fields on user instance.
Inherit from django BaseUserAdmin
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
# The forms to add and change user instances
add_form = UserAdminForm
@ -179,6 +252,15 @@ class AdherentAdmin(VersionAdmin, BaseUserAdmin):
class ClubAdmin(VersionAdmin, BaseUserAdmin):
"""Club Admin view and management, for
Club fields : password, pseudo, etc, admin can
edit all fields on user instance.
Inherit from django BaseUserAdmin
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
# The forms to add and change user instances
add_form = UserAdminForm
form = UserAdminForm
@ -225,8 +307,15 @@ class ClubAdmin(VersionAdmin, BaseUserAdmin):
class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
"""Gestion d'un service user admin : champs personnels,
mot de passe; etc"""
"""ServiceUser Admin view and management, for
User fields : password, pseudo, etc, admin can
edit all fields on user instance.
Inherit from django BaseUserAdmin
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
# The forms to add and change user instances
form = ServiceUserAdminForm

View file

@ -23,14 +23,20 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Definition des forms pour l'application users.
Forms for the 'users' app of re2o. It highly depends on
:users:models and is mainly used by :users:views.
Modification, creation de :
- un user (informations personnelles)
- un bannissement
- le mot de passe d'un user
- une whiteliste
- un user de service
The following forms are mainly used to create, edit or delete
anything related to 'users' :
* Adherent (personnal data)
* Club
* Ban
* ServiceUser
* Whitelists
* ...
See the details for each of these operations in the documentation
of each of the method.
"""
from __future__ import unicode_literals
@ -72,62 +78,16 @@ from .models import (
)
class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
"""Formulaire de changement de mot de passe. Verifie que les 2
nouveaux mots de passe renseignés sont identiques et respectent
une norme"""
selfpasswd = forms.CharField(
label=_("Current password"), max_length=255, widget=forms.PasswordInput
)
passwd1 = forms.CharField(
label=_("New password"),
max_length=255,
widget=forms.PasswordInput,
help_text=password_validators_help_text_html()
)
passwd2 = forms.CharField(
label=_("New password confirmation"),
max_length=255,
widget=forms.PasswordInput,
)
class Meta:
model = User
fields = []
def clean_passwd2(self):
"""Verifie que passwd1 et 2 sont identiques"""
# Check that the two password entries match
password1 = self.cleaned_data.get("passwd1")
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):
"""Verifie si il y a lieu que le mdp self est correct"""
if not self.instance.check_password(self.cleaned_data.get("selfpasswd")):
raise forms.ValidationError(_("The current password is incorrect."))
return
def save(self, commit=True):
"""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()
#### Django Admin Custom Views
class UserAdminForm(FormRevMixin, forms.ModelForm):
"""A form for creating new and editing users. Includes all the required
fields, plus a repeated password.
Formulaire pour la création d'un user. N'est utilisé que pour
l'admin, lors de la creation d'un user par admin. Inclu tous les
champs obligatoires"""
Parameters:
DjangoForm : Inherit from basic django form
"""
password1 = forms.CharField(
label=_("Password"),
@ -152,8 +112,14 @@ class UserAdminForm(FormRevMixin, forms.ModelForm):
fields = ("pseudo", "surname", "name", "email", "is_superuser")
def clean_password2(self):
"""Verifie que password1 et 2 sont identiques"""
# Check that the two password entries match
"""Clean password 2, check if passwd1 and 2 values match.
Parameters:
self : Apply on a django Form UserCreationForm instance
Returns:
password2 (string): The password2 value if all tests returned True
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2:
@ -163,6 +129,13 @@ class UserAdminForm(FormRevMixin, forms.ModelForm):
return password2
def save(self, commit=True):
"""Save function. Call standard "set_password" django function,
from provided value for new password, for making hash.
Parameters:
self : Apply on a django Form UserCreationForm instance
commit : If False, don't make the real save in database
"""
# Save the provided password in hashed format
user = super(UserAdminForm, self).save(commit=False)
if self.cleaned_data["password1"]:
@ -172,11 +145,12 @@ class UserAdminForm(FormRevMixin, forms.ModelForm):
class ServiceUserAdminForm(FormRevMixin, forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password.
"""A form for creating new service users. Includes all the required
fields, plus a repeated password. For Admin view purpose only.
Formulaire pour la creation de nouveaux serviceusers.
Requiert seulement un mot de passe; et un pseudo"""
Parameters:
DjangoForm : Inherit from basic django form
"""
password1 = forms.CharField(
label=_("Password"),
@ -198,8 +172,14 @@ class ServiceUserAdminForm(FormRevMixin, forms.ModelForm):
fields = ("pseudo",)
def clean_password2(self):
"""Verifie que password1 et 2 sont identiques"""
# Check that the two password entries match
"""Clean password 2, check if passwd1 and 2 values match.
Parameters:
self : Apply on a django Form UserCreationForm instance
Returns:
password2 (string): The password2 value if all tests returned True
"""
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
@ -207,25 +187,111 @@ class ServiceUserAdminForm(FormRevMixin, forms.ModelForm):
return password2
def save(self, commit=True):
# Save the provided password in hashed format
"""Save function. Call standard "set_password" django function,
from provided value for new password, for making hash.
Parameters:
self : Apply on a django Form ServiceUserAdminForm instance
commit : If False, don't make the real save in database
"""
user = super(ServiceUserAdminForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.save()
return user
### Classic Django View
class PassForm(FormRevMixin, FieldPermissionFormMixin, forms.ModelForm):
"""Django form for changing password, check if 2 passwords are the same,
and validate password for django base password validators provided in
settings_local.
Parameters:
DjangoForm : Inherit from basic django form
"""
selfpasswd = forms.CharField(
label=_("Current password"), max_length=255, widget=forms.PasswordInput
)
passwd1 = forms.CharField(
label=_("New password"),
max_length=255,
widget=forms.PasswordInput,
help_text=password_validators_help_text_html()
)
passwd2 = forms.CharField(
label=_("New password confirmation"),
max_length=255,
widget=forms.PasswordInput,
)
class Meta:
model = User
fields = []
def clean_passwd2(self):
"""Clean password 2, check if passwd1 and 2 values match, and
apply django validator with validate_password function.
Parameters:
self : Apply on a django Form PassForm instance
Returns:
password2 (string): The password2 value if all tests returned True
"""
password1 = self.cleaned_data.get("passwd1")
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):
"""Clean selfpassword, check if provided original user password match
with the stored value.
Parameters:
self : Apply on a django Form PassForm instance
"""
if not self.instance.check_password(self.cleaned_data.get("selfpasswd")):
raise forms.ValidationError(_("The current password is incorrect."))
return
def save(self, commit=True):
"""Save function. Call standard "set_password" django function,
and call set_active for set user in active state if needed.
Parameters:
self : Apply on a django Form PassForm instance
commit : If False, don't make the real save in database
"""
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()
class ResetPasswordForm(forms.Form):
"""Formulaire de demande de reinitialisation de mot de passe,
mdp oublié"""
"""A form for asking to reset password.
Parameters:
DjangoForm : Inherit from basic django form
"""
pseudo = forms.CharField(label=_("Username"), max_length=255)
email = forms.EmailField(max_length=255)
class MassArchiveForm(forms.Form):
"""Formulaire d'archivage des users inactif. Prend en argument
du formulaire la date de depart avant laquelle archiver les
users"""
"""A form for archiving a lot de users. Get a start date
for start archiving.
Parameters:
DjangoForm : Inherit from basic django form
"""
date = forms.DateTimeField(help_text="%d/%m/%y")
full_archive = forms.BooleanField(
@ -251,9 +317,12 @@ class MassArchiveForm(forms.Form):
class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis"""
"""Adherent Edition Form, base form used for editing user by himself
or another user. Labels are provided for help purposes.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -288,24 +357,42 @@ class AdherentForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
)
def clean_telephone(self):
"""Verifie que le tel est présent si 'option est validée
dans preferences"""
"""Clean telephone, check if telephone is made mandatory, and
raise error if not provided
Parameters:
self : Apply on a django Form AdherentForm instance
Returns:
telephone (string): The telephone string if clean is True
"""
telephone = self.cleaned_data["telephone"]
if not telephone and OptionalUser.get_cached_value("is_tel_mandatory"):
raise forms.ValidationError(_("A valid telephone number is required."))
return telephone
def clean_force(self):
"""On supprime l'ancien user de la chambre si et seulement si la
case est cochée"""
"""Clean force, remove previous user from room if needed.
Parameters:
self : Apply on a django Form AdherentForm instance
"""
room = self.cleaned_data.get("room")
if self.cleaned_data.get("force", False) and room:
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"""
"""Clean room, based on room policy provided by preferences.
If needed, call remove_user_room to make the room empty before
saving self.instance into that room.
Parameters:
self : Apply on a django Form AdherentForm instance
Returns:
room (string): The room instance
"""
# Handle case where regular users can force move
room = self.cleaned_data.get("room")
room_policy = OptionalUser.get_cached_value("self_room_policy")
@ -320,10 +407,13 @@ 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 et, optionnellement,
un champ mot de passe"""
"""AdherentCreationForm. Inherit from AdherentForm, base form used for creating
user by himself or another user. Labels are provided for help purposes.
Add some instructions, and validation for initial creation.
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"
@ -407,7 +497,15 @@ class AdherentCreationForm(AdherentForm):
self.fields.pop("password2")
def clean_password2(self):
"""Verifie que password1 et 2 sont identiques (si nécessaire)"""
"""Clean password 2, check if passwd1 and 2 values match, and
apply django validator with validate_password function.
Parameters:
self : Apply on a django Form AdherentCreationForm instance
Returns:
password2 (string): The password2 value if all tests returned True
"""
send_email = self.cleaned_data.get("init_password_by_mail")
if send_email:
return None
@ -421,9 +519,14 @@ class AdherentCreationForm(AdherentForm):
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 function. If password has been set during creation,
call standard "set_password" django function from provided value
for new password, for making hash.
Parameters:
self : Apply on a django Form AdherentCreationForm instance
commit : If False, don't make the real save in database
"""
# Save the provided password in hashed format
user = super(AdherentForm, self).save(commit=False)
@ -437,8 +540,13 @@ class AdherentCreationForm(AdherentForm):
class AdherentEditForm(AdherentForm):
"""Formulaire d'édition d'un user.
AdherentForm incluant la modification des champs gpg et shell"""
"""AdherentEditForm. Inherit from AdherentForm, base form used for editing
user by himself or another user. Labels are provided for help purposes.
Add some instructions, and validation, fields depends on editing user rights.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
super(AdherentEditForm, self).__init__(*args, **kwargs)
@ -469,9 +577,13 @@ class AdherentEditForm(AdherentForm):
class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Formulaire de base d'edition d'un user. Formulaire de base, utilisé
pour l'edition de self par self ou un cableur. On formate les champs
avec des label plus jolis"""
"""ClubForm. For editing club by himself or another user. Labels are provided for
help purposes. Add some instructions, and validation, fields depends
on editing user rights.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -503,8 +615,15 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
]
def clean_telephone(self):
"""Verifie que le tel est présent si 'option est validée
dans preferences"""
"""Clean telephone, check if telephone is made mandatory, and
raise error if not provided
Parameters:
self : Apply on a django Form ClubForm instance
Returns:
telephone (string): The telephone string if clean is True
"""
telephone = self.cleaned_data["telephone"]
if not telephone and OptionalUser.get_cached_value("is_tel_mandatory"):
raise forms.ValidationError(_("A valid telephone number is required."))
@ -512,8 +631,12 @@ class ClubForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class ClubAdminandMembersForm(FormRevMixin, ModelForm):
"""Permet d'éditer la liste des membres et des administrateurs
d'un club"""
"""ClubAdminandMembersForm. Only For editing administrators of a club by himself
or another user.
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta:
model = Club
@ -525,8 +648,11 @@ class ClubAdminandMembersForm(FormRevMixin, ModelForm):
class PasswordForm(FormRevMixin, ModelForm):
""" Formulaire de changement brut de mot de passe.
Ne pas utiliser sans traitement"""
"""PasswordForm. Do not use directly in views without extra validations.
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta:
model = User
@ -538,8 +664,12 @@ class PasswordForm(FormRevMixin, ModelForm):
class ServiceUserForm(FormRevMixin, ModelForm):
"""Service user creation
force initial password set"""
"""ServiceUserForm, used for creating a service user, require
a password and set it.
Parameters:
DjangoForm : Inherit from basic django form
"""
password = forms.CharField(
label=_("New password"),
@ -558,7 +688,14 @@ class ServiceUserForm(FormRevMixin, ModelForm):
super(ServiceUserForm, self).__init__(*args, prefix=prefix, **kwargs)
def save(self, commit=True):
"""Password change"""
"""Save function. If password has been changed and provided,
call standard "set_password" django function from provided value
for new password, for making hash.
Parameters:
self : Apply on a django Form ServiceUserForm instance
commit : If False, don't make the real save in database
"""
user = super(ServiceUserForm, self).save(commit=False)
if self.cleaned_data["password"]:
user.set_password(self.cleaned_data.get("password"))
@ -566,8 +703,12 @@ class ServiceUserForm(FormRevMixin, ModelForm):
class EditServiceUserForm(ServiceUserForm):
"""Formulaire d'edition de base d'un service user. Ne permet
d'editer que son group d'acl et son commentaire"""
"""EditServiceUserForm, used for editing a service user, can
edit password, access_group and comment.
Parameters:
DjangoForm : Inherit from basic django form
"""
password = forms.CharField(
label=_("New password"),
@ -582,7 +723,12 @@ class EditServiceUserForm(ServiceUserForm):
class StateForm(FormRevMixin, ModelForm):
"""Change state of an user, and if its main email is verified or not"""
"""StateForm, Change state of an user, and if
its main email is verified or not
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta:
model = User
@ -596,7 +742,11 @@ class StateForm(FormRevMixin, ModelForm):
class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
""" Gestion des groupes d'un user"""
"""GroupForm, form used for editing user groups.
Parameters:
DjangoForm : Inherit from basic django form
"""
groups = forms.ModelMultipleChoiceField(
Group.objects.all(), widget=forms.CheckboxSelectMultiple, required=False
@ -614,7 +764,11 @@ class GroupForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
class SchoolForm(FormRevMixin, ModelForm):
"""Edition, creation d'un école"""
"""SchoolForm, form used for creating or editing school.
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta:
model = School
@ -627,7 +781,11 @@ class SchoolForm(FormRevMixin, ModelForm):
class ShellForm(FormRevMixin, ModelForm):
"""Edition, creation d'un école"""
"""ShellForm, form used for creating or editing shell.
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta:
model = ListShell
@ -640,8 +798,13 @@ class ShellForm(FormRevMixin, ModelForm):
class ListRightForm(FormRevMixin, ModelForm):
"""Edition, d'un groupe , équivalent à un droit
Ne permet pas d'editer le gid, car il sert de primary key"""
"""ListRightForm, form used for editing a listright,
related with django group object. Gid, primary key, can't
be edited.
Parameters:
DjangoForm : Inherit from basic django form
"""
permissions = forms.ModelMultipleChoiceField(
Permission.objects.all().select_related("content_type"),
@ -660,7 +823,12 @@ class ListRightForm(FormRevMixin, ModelForm):
class NewListRightForm(ListRightForm):
"""Ajout d'un groupe/list de droit """
"""ListRightForm, form used for creating a listright,
related with django group object.
Parameters:
DjangoForm : Inherit from basic django form
"""
class Meta(ListRightForm.Meta):
fields = ("name", "unix_name", "gid", "critical", "permissions", "details")
@ -673,7 +841,12 @@ class NewListRightForm(ListRightForm):
class DelListRightForm(Form):
"""Suppression d'un ou plusieurs groupes"""
"""DelListRightForm, form for deleting one or several ListRight
instances.
Parameters:
DjangoForm : Inherit from basic django form
"""
listrights = forms.ModelMultipleChoiceField(
queryset=ListRight.objects.none(),
@ -691,7 +864,12 @@ class DelListRightForm(Form):
class DelSchoolForm(Form):
"""Suppression d'une ou plusieurs écoles"""
"""DelSchoolForm, form for deleting one or several School
instances.
Parameters:
DjangoForm : Inherit from basic django form
"""
schools = forms.ModelMultipleChoiceField(
queryset=School.objects.none(),
@ -709,7 +887,11 @@ class DelSchoolForm(Form):
class BanForm(FormRevMixin, ModelForm):
"""Creation, edition d'un objet bannissement"""
"""BanForm, form used for creating or editing a ban instance.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -724,7 +906,11 @@ class BanForm(FormRevMixin, ModelForm):
class WhitelistForm(FormRevMixin, ModelForm):
"""Creation, edition d'un objet whitelist"""
"""WhitelistForm, form used for creating or editing a whitelist instance.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -739,7 +925,12 @@ class WhitelistForm(FormRevMixin, ModelForm):
class EMailAddressForm(FormRevMixin, ModelForm):
"""Create and edit a local email address"""
"""EMailAddressForm, form used for creating or editing a local
email for a user.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -756,7 +947,11 @@ class EMailAddressForm(FormRevMixin, ModelForm):
class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Edit email-related settings"""
"""EMailSettingsForm, form used for editing email settings for a user.
Parameters:
DjangoForm : Inherit from basic django form
"""
def __init__(self, *args, **kwargs):
prefix = kwargs.pop("prefix", self.Meta.model.__name__)
@ -775,6 +970,12 @@ class EmailSettingsForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class InitialRegisterForm(forms.Form):
"""InitialRegisterForm, form used for auto-register of room and mac-address
with captive-portal.
Parameters:
DjangoForm : Inherit from basic django form
"""
register_room = forms.BooleanField(required=False)
register_machine = forms.BooleanField(required=False)
@ -818,6 +1019,13 @@ class InitialRegisterForm(forms.Form):
self.fields.pop("register_machine")
def clean_register_room(self):
"""Clean room, call remove_user_room to make the room empty before
saving self.instance into that room.
Parameters:
self : Apply on a django Form InitialRegisterForm instance
"""
if self.cleaned_data["register_room"]:
if self.user.is_class_adherent:
remove_user_room(self.new_room)
@ -830,6 +1038,12 @@ class InitialRegisterForm(forms.Form):
user.save()
def clean_register_machine(self):
"""Clean register room, autoregister machine from user request mac_address.
Parameters:
self : Apply on a django Form InitialRegisterForm instance
"""
if self.cleaned_data["register_machine"]:
if self.mac_address and self.nas_type:
self.user.autoregister_machine(self.mac_address, self.nas_type)

File diff suppressed because it is too large Load diff

View file

@ -23,7 +23,7 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Definition des urls, pointant vers les views
The defined URLs for the Users app
"""
from __future__ import unicode_literals

View file

@ -27,13 +27,30 @@
# Lara Kermarec, Gabriel Détraz, Lemesle Augustin
# Gplv2
"""
Module des views.
Django users views module.
On définit les vues pour l'ajout, l'edition des users : infos personnelles,
mot de passe, etc
Here are defined all functions of views, for the users re2o application. This views
allow both edition, creation, deletion and diplay of users objects.
Here are view that allow the addition/deletion/edition of:
* Users (Club/Adherent) and derived settings like EmailSettings of users
* School
* Bans
* Whitelist
* Shell
* ServiceUser
Also add extra views for :
* Ask for reset password by email
* Ask for new email for email confirmation
* Register room and interface on user account with switch web redirection.
All the view must be as simple as possible, with returning the correct form to user during
get, and during post, performing change in database with simple ".save()" function.
The aim is to put all "intelligent" functions in both forms and models functions. In fact, this
will allow to user other frontend (like REST api) to perform editions, creations, etc on database,
without code duplication.
Permet aussi l'ajout, edition et suppression des droits, des bannissements,
des whitelist, des services users et des écoles
"""
from __future__ import unicode_literals
@ -117,8 +134,17 @@ import os
@can_create(Adherent)
def new_user(request):
""" Vue de création d'un nouvel utilisateur,
envoie un mail pour le mot de passe"""
"""View for new Adherent/User form creation.
Then, send an email to the new user, and also if needed to
set its password.
Parameters:
request (django request): Standard django request.
Returns:
Django User form.
"""
user = AdherentCreationForm(request.POST or None, request.FILES or None, user=request.user)
user.request = request
@ -167,8 +193,17 @@ def new_user(request):
@login_required
@can_create(Club)
def new_club(request):
""" Vue de création d'un nouveau club,
envoie un mail pour le mot de passe"""
"""View for new Club/User form creation.
Then, send an email to the new user, and also if needed to
set its password.
Parameters:
request (django request): Standard django request.
Returns:
Django User form.
"""
club = ClubForm(request.POST or None, request.FILES or None, user=request.user)
club.request = request
@ -192,8 +227,16 @@ def new_club(request):
@login_required
@can_edit(Club)
def edit_club_admin_members(request, club_instance, **_kwargs):
"""Vue d'edition de la liste des users administrateurs et
membres d'un club"""
"""View for editing clubs and administrators.
Parameters:
request (django request): Standard django request.
club_instance: Club instance to edit
Returns:
Django User form.
"""
club = ClubAdminandMembersForm(request.POST or None, request.FILES or None, instance=club_instance)
if club.is_valid():
if club.changed_data:
@ -212,9 +255,17 @@ def edit_club_admin_members(request, club_instance, **_kwargs):
@login_required
@can_edit(User)
def edit_info(request, user, userid):
""" Edite un utilisateur à partir de son id,
si l'id est différent de request.user, vérifie la
possession du droit cableur """
"""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.
"""
if user.is_class_adherent:
user_form = AdherentEditForm(
request.POST or None, request.FILES or None, instance=user.adherent, user=request.user
@ -246,7 +297,18 @@ def edit_info(request, user, userid):
@login_required
@can_edit(User, "state")
def state(request, user, userid):
""" Change the state (active/unactive/archived) of a user"""
"""View for editing state of user.
Perform an acl check on user instance, and check if editing user
has state edition permission.
Parameters:
request (django request): Standard django request.
user: User instance to edit
Returns:
Django User form.
"""
state_form = StateForm(request.POST or None, instance=user)
if state_form.is_valid():
if state_form.changed_data:
@ -265,7 +327,18 @@ def state(request, user, userid):
@login_required
@can_edit(User, "groups")
def groups(request, user, userid):
""" View to edit the groups of a user """
"""View for editing groups of user.
Perform an acl check on user instance, and check if editing user
has groups edition permission.
Parameters:
request (django request): Standard django request.
user: User instance to edit
Returns:
Django User form.
"""
group_form = GroupForm(request.POST or None, instance=user, user=request.user)
if group_form.is_valid():
if group_form.changed_data:
@ -280,9 +353,20 @@ def groups(request, user, userid):
@login_required
@can_edit(User, "password")
def password(request, user, userid):
""" Reinitialisation d'un mot de passe à partir de l'userid,
pour self par défaut, pour tous sans droit si droit cableur,
pour tous si droit bureau """
"""View for editing password of user.
Perform an acl check on user instance, and check if editing user
has password edition permission.
If User instance is in critical groups, the edition requires extra
permission.
Parameters:
request (django request): Standard django request.
user: User instance to edit password
Returns:
Django User form.
"""
u_form = PassForm(request.POST or None, instance=user, user=request.user)
if u_form.is_valid():
if u_form.changed_data:
@ -299,7 +383,20 @@ def password(request, user, userid):
@login_required
@can_edit(User, "groups")
def del_group(request, user, listrightid, **_kwargs):
""" View used to delete a group """
"""View for editing groups of user.
Perform an acl check on user instance, and check if editing user
has groups edition permission.
If User instance is in critical groups, the edition requires extra
permission.
Parameters:
request (django request): Standard django request.
user: User instance to edit groups
Returns:
Django User form.
"""
user.groups.remove(ListRight.objects.get(id=listrightid))
user.save()
messages.success(request, _("%s was removed from the group.") % user)
@ -309,7 +406,18 @@ def del_group(request, user, listrightid, **_kwargs):
@login_required
@can_edit(User, "is_superuser")
def del_superuser(request, user, **_kwargs):
"""Remove the superuser right of an user."""
"""View for editing superuser attribute of user.
Perform an acl check on user instance, and check if editing user
has edition of superuser flag on target user.
Parameters:
request (django request): Standard django request.
user: User instance to edit superuser flag.
Returns:
Django User form.
"""
user.is_superuser = False
user.save()
messages.success(request, _("%s is no longer superuser.") % user)
@ -319,7 +427,18 @@ def del_superuser(request, user, **_kwargs):
@login_required
@can_create(ServiceUser)
def new_serviceuser(request):
""" Vue de création d'un nouvel utilisateur service"""
"""View for creation of new serviceuser, for external services on
ldap tree for auth purpose (dokuwiki, owncloud, etc).
Perform an acl check on editing user, and check if editing user
has permission of create new serviceuser.
Parameters:
request (django request): Standard django request.
Returns:
Django ServiceUser form.
"""
user = ServiceUserForm(request.POST or None)
if user.is_valid():
user.save()
@ -333,7 +452,19 @@ def new_serviceuser(request):
@login_required
@can_edit(ServiceUser)
def edit_serviceuser(request, serviceuser, **_kwargs):
""" Edit a ServiceUser """
"""View for edition of serviceuser, for external services on
ldap tree for auth purpose (dokuwiki, owncloud, etc).
Perform an acl check on editing user, and check if editing user
has permission of edit target serviceuser.
Parameters:
request (django request): Standard django request.
serviceuser: ServiceUser instance to edit attributes.
Returns:
Django ServiceUser form.
"""
serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser)
if serviceuser.is_valid():
if serviceuser.changed_data:
@ -348,7 +479,19 @@ def edit_serviceuser(request, serviceuser, **_kwargs):
@login_required
@can_delete(ServiceUser)
def del_serviceuser(request, serviceuser, **_kwargs):
"""Suppression d'un ou plusieurs serviceusers"""
"""View for removing serviceuser, for external services on
ldap tree for auth purpose (dokuwiki, owncloud, etc).
Perform an acl check on editing user, and check if editing user
has permission of deleting target serviceuser.
Parameters:
request (django request): Standard django request.
serviceuser: ServiceUser instance to delete.
Returns:
Django ServiceUser form.
"""
if request.method == "POST":
serviceuser.delete()
messages.success(request, _("The service user was deleted."))
@ -364,9 +507,19 @@ def del_serviceuser(request, serviceuser, **_kwargs):
@can_create(Ban)
@can_edit(User)
def add_ban(request, user, userid):
""" Ajouter un banissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
"""View for adding a ban object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of adding a ban on target user, add_ban.
Syntaxe: DD/MM/AAAA, the ban takes an immediate effect.
Parameters:
request (django request): Standard django request.
user: User instance to add a ban.
Returns:
Django Ban form.
"""
ban_instance = Ban(user=user)
ban = BanForm(request.POST or None, instance=ban_instance)
ban.request = request
@ -383,9 +536,19 @@ def add_ban(request, user, userid):
@login_required
@can_edit(Ban)
def edit_ban(request, ban_instance, **_kwargs):
""" Editer un bannissement, nécessite au moins le droit bofh
(a fortiori bureau)
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement"""
"""View for editing a ban object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of editing a ban on target user, edit_ban.
Syntaxe: DD/MM/AAAA, the ban takes an immediate effect.
Parameters:
request (django request): Standard django request.
ban: Ban instance to edit.
Returns:
Django Ban form.
"""
ban = BanForm(request.POST or None, instance=ban_instance)
ban.request = request
@ -400,7 +563,18 @@ def edit_ban(request, ban_instance, **_kwargs):
@login_required
@can_delete(Ban)
def del_ban(request, ban, **_kwargs):
""" Supprime un banissement"""
"""View for removing a ban object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of deleting a ban on target user, del_ban.
Parameters:
request (django request): Standard django request.
ban: Ban instance to delete.
Returns:
Django Ban form.
"""
if request.method == "POST":
ban.delete()
messages.success(request, _("The ban was deleted."))
@ -412,10 +586,19 @@ def del_ban(request, ban, **_kwargs):
@can_create(Whitelist)
@can_edit(User)
def add_whitelist(request, user, userid):
""" Accorder un accès gracieux, temporaire ou permanent.
Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
"""View for adding a whitelist object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of adding a wheitelist on target user, add_whitelist.
Syntaxe: DD/MM/AAAA, the whitelist takes an immediate effect.
Parameters:
request (django request): Standard django request.
user: User instance to add a whitelist.
Returns:
Django Whitelist form.
"""
whitelist_instance = Whitelist(user=user)
whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance)
if whitelist.is_valid():
@ -434,10 +617,19 @@ def add_whitelist(request, user, userid):
@login_required
@can_edit(Whitelist)
def edit_whitelist(request, whitelist_instance, **_kwargs):
""" Editer un accès gracieux, temporaire ou permanent.
Need droit cableur
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement,
raison obligatoire"""
"""View for editing a whitelist object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of editing a whitelist on target user, edit_whitelist.
Syntaxe: DD/MM/AAAA, the whitelist takes an immediate effect.
Parameters:
request (django request): Standard django request.
whitelist: whitelist instance to edit.
Returns:
Django Whitelist form.
"""
whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance)
if whitelist.is_valid():
if whitelist.changed_data:
@ -452,7 +644,18 @@ def edit_whitelist(request, whitelist_instance, **_kwargs):
@login_required
@can_delete(Whitelist)
def del_whitelist(request, whitelist, **_kwargs):
""" Supprime un acces gracieux"""
"""View for removing a whitelist object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of deleting a whitelist on target user, del_whitelist.
Parameters:
request (django request): Standard django request.
whitelist: Whitelist instance to delete.
Returns:
Django Whitelist form.
"""
if request.method == "POST":
whitelist.delete()
messages.success(request, _("The whitelist was deleted."))
@ -468,7 +671,18 @@ def del_whitelist(request, whitelist, **_kwargs):
@can_create(EMailAddress)
@can_edit(User)
def add_emailaddress(request, user, userid):
""" Create a new local email account"""
"""View for adding an emailaddress object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of adding an emailaddress on target user.
Parameters:
request (django request): Standard django request.
user: User instance to add an emailaddress.
Returns:
Django EmailAddress form.
"""
emailaddress_instance = EMailAddress(user=user)
emailaddress = EMailAddressForm(
request.POST or None, instance=emailaddress_instance
@ -487,7 +701,18 @@ def add_emailaddress(request, user, userid):
@login_required
@can_edit(EMailAddress)
def edit_emailaddress(request, emailaddress_instance, **_kwargs):
""" Edit a local email account"""
"""View for edit an emailaddress object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of editing an emailaddress on target user.
Parameters:
request (django request): Standard django request.
emailaddress: Emailaddress to edit.
Returns:
Django EmailAddress form.
"""
emailaddress = EMailAddressForm(
request.POST or None, instance=emailaddress_instance
)
@ -510,7 +735,18 @@ def edit_emailaddress(request, emailaddress_instance, **_kwargs):
@login_required
@can_delete(EMailAddress)
def del_emailaddress(request, emailaddress, **_kwargs):
"""Delete a local email account"""
"""View for deleting an emailaddress object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of deleting an emailaddress on target user.
Parameters:
request (django request): Standard django request.
emailaddress: Emailaddress to delete.
Returns:
Django EmailAddress form.
"""
if request.method == "POST":
emailaddress.delete()
messages.success(request, _("The local email account was deleted."))
@ -527,7 +763,18 @@ def del_emailaddress(request, emailaddress, **_kwargs):
@login_required
@can_edit(User)
def edit_email_settings(request, user_instance, **_kwargs):
"""Edit the email settings of a user"""
"""View for editing User's emailaddress settings for user instance.
Perform an acl check on editing user, and check if editing user
has permission of editing email settings on target user.
Parameters:
request (django request): Standard django request.
user: User instance to edit email settings.
Returns:
Django User form.
"""
email_settings = EmailSettingsForm(
request.POST or None, instance=user_instance, user=request.user
)
@ -559,8 +806,17 @@ def edit_email_settings(request, user_instance, **_kwargs):
@login_required
@can_create(School)
def add_school(request):
""" Ajouter un établissement d'enseignement à la base de donnée,
need cableur"""
"""View for adding a new school object.
Perform an acl check on editing user, and check if editing user
has permission of adding a new school, add_school.
Parameters:
request (django request): Standard django request.
Returns:
Django School form.
"""
school = SchoolForm(request.POST or None)
if school.is_valid():
school.save()
@ -574,8 +830,18 @@ def add_school(request):
@login_required
@can_edit(School)
def edit_school(request, school_instance, **_kwargs):
""" Editer un établissement d'enseignement à partir du schoolid dans
la base de donnée, need cableur"""
"""View for editing a school instance object.
Perform an acl check on editing user, and check if editing user
has permission of editing a school, edit_school.
Parameters:
request (django request): Standard django request.
school_instance: school instance to edit.
Returns:
Django School form.
"""
school = SchoolForm(request.POST or None, instance=school_instance)
if school.is_valid():
if school.changed_data:
@ -590,10 +856,20 @@ def edit_school(request, school_instance, **_kwargs):
@login_required
@can_delete_set(School)
def del_school(request, instances):
""" Supprimer un établissement d'enseignement à la base de donnée,
need cableur
Objet protégé, possible seulement si aucun user n'est affecté à
l'établissement """
"""View for deleting a school instance object.
Perform an acl check on editing user, and check if editing user
has permission of deleting a school, del_school.
A school can be deleted only if it is not assigned to a user (mode
protect).
Parameters:
request (django request): Standard django request.
school_instance: school instance to delete.
Returns:
Django School form.
"""
school = DelSchoolForm(request.POST or None, instances=instances)
if school.is_valid():
school_dels = school.cleaned_data["schools"]
@ -619,7 +895,17 @@ def del_school(request, instances):
@login_required
@can_create(ListShell)
def add_shell(request):
""" Ajouter un shell à la base de donnée"""
"""View for adding a new linux shell object.
Perform an acl check on editing user, and check if editing user
has permission of adding a new shell, add_school.
Parameters:
request (django request): Standard django request.
Returns:
Django Shell form.
"""
shell = ShellForm(request.POST or None)
if shell.is_valid():
shell.save()
@ -633,7 +919,18 @@ def add_shell(request):
@login_required
@can_edit(ListShell)
def edit_shell(request, shell_instance, **_kwargs):
""" Editer un shell à partir du listshellid"""
"""View for editing a shell instance object.
Perform an acl check on editing user, and check if editing user
has permission of editing a shell, edit_shell.
Parameters:
request (django request): Standard django request.
shell_instance: shell instance to edit.
Returns:
Django Shell form.
"""
shell = ShellForm(request.POST or None, instance=shell_instance)
if shell.is_valid():
if shell.changed_data:
@ -648,7 +945,20 @@ def edit_shell(request, shell_instance, **_kwargs):
@login_required
@can_delete(ListShell)
def del_shell(request, shell, **_kwargs):
"""Destruction d'un shell"""
"""View for deleting a shell instance object.
Perform an acl check on editing user, and check if editing user
has permission of deleting a shell, del_shell.
A shell can be deleted only if it is not assigned to a user (mode
protect).
Parameters:
request (django request): Standard django request.
shell_instance: shell instance to delete.
Returns:
Django Shell form.
"""
if request.method == "POST":
shell.delete()
messages.success(request, _("The shell was deleted."))
@ -661,8 +971,18 @@ def del_shell(request, shell, **_kwargs):
@login_required
@can_create(ListRight)
def add_listright(request):
""" Ajouter un droit/groupe, nécessite droit bureau.
Obligation de fournir un gid pour la synchro ldap, unique """
"""View for adding a new group of rights and users (listright linked to groups)
object for user instance.
Perform an acl check on editing user, and check if editing user
has permission of adding a new listright.
Parameters:
request (django request): Standard django request.
Returns:
Django ListRight form.
"""
listright = NewListRightForm(request.POST or None)
if listright.is_valid():
listright.save()
@ -678,8 +998,18 @@ def add_listright(request):
@login_required
@can_edit(ListRight)
def edit_listright(request, listright_instance, **_kwargs):
""" Editer un groupe/droit, necessite droit bureau,
à partir du listright id """
"""View for editing a listright instance object.
Perform an acl check on editing user, and check if editing user
has permission of editing a listright, edit_listright.
Parameters:
request (django request): Standard django request.
listright_instance: listright instance to edit.
Returns:
Django ListRight form.
"""
listright_form = ListRightForm(request.POST or None, instance=listright_instance)
if listright_form.is_valid():
if listright_form.changed_data:
@ -701,8 +1031,20 @@ def edit_listright(request, listright_instance, **_kwargs):
@login_required
@can_delete_set(ListRight)
def del_listright(request, instances):
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit
bureau """
"""View for deleting a listright instance object.
Perform an acl check on editing user, and check if editing user
has permission of deleting a listright, del_listright.
A listright/group can be deleted only if it is empty (mode
protect).
Parameters:
request (django request): Standard django request.
listright_instance: listright instance to delete.
Returns:
Django ListRight form.
"""
listright = DelListRightForm(request.POST or None, instances=instances)
if listright.is_valid():
listright_dels = listright.cleaned_data["listrights"]
@ -729,7 +1071,17 @@ def del_listright(request, instances):
@can_view_all(User)
@can_change(User, "state")
def mass_archive(request):
""" Permet l'archivage massif"""
"""View for performing a mass archive operation.
Check if editing User has the acl for globaly changing "State"
flag on users, and can edit all the users.
Parameters:
request (django request): Standard django request.
Returns:
Django User form.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
to_archive_form = MassArchiveForm(request.POST or None)
to_archive_list = []
@ -764,7 +1116,16 @@ def mass_archive(request):
@login_required
@can_view_all(Adherent)
def index(request):
""" Affiche l'ensemble des adherents, need droit cableur """
"""View for displaying the paginated list of all users/adherents in re2o.
Need the global acl for viewing all users, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django Adherent Form.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
users_list = Adherent.objects.select_related("room")
users_list = SortTable.sort(
@ -780,7 +1141,16 @@ def index(request):
@login_required
@can_view_all(Club)
def index_clubs(request):
""" Affiche l'ensemble des clubs, need droit cableur """
"""View for displaying the paginated list of all users/clubs in re2o.
Need the global acl for viewing all users, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django Adherent Form.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
clubs_list = Club.objects.select_related("room")
clubs_list = SortTable.sort(
@ -796,7 +1166,16 @@ def index_clubs(request):
@login_required
@can_view_all(Ban)
def index_ban(request):
""" Affiche l'ensemble des ban, need droit cableur """
"""View for displaying the paginated list of all bans in re2o.
Need the global acl for viewing all bans, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django Ban Form.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
ban_list = Ban.objects.select_related("user")
ban_list = SortTable.sort(
@ -812,7 +1191,16 @@ def index_ban(request):
@login_required
@can_view_all(Whitelist)
def index_white(request):
""" Affiche l'ensemble des whitelist, need droit cableur """
"""View for displaying the paginated list of all whitelists in re2o.
Need the global acl for viewing all whitelists, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django Whitelist Form.
"""
pagination_number = GeneralOption.get_cached_value("pagination_number")
white_list = Whitelist.objects.select_related("user")
white_list = SortTable.sort(
@ -828,7 +1216,16 @@ def index_white(request):
@login_required
@can_view_all(School)
def index_school(request):
""" Affiche l'ensemble des établissement"""
"""View for displaying the paginated list of all schools in re2o.
Need the global acl for viewing all schools, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django School Form.
"""
school_list = School.objects.order_by("name")
pagination_number = GeneralOption.get_cached_value("pagination_number")
school_list = SortTable.sort(
@ -844,7 +1241,16 @@ def index_school(request):
@login_required
@can_view_all(ListShell)
def index_shell(request):
""" Affiche l'ensemble des shells"""
"""View for displaying the paginated list of all shells in re2o.
Need the global acl for viewing all shells, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django Shell Form.
"""
shell_list = ListShell.objects.order_by("shell")
return render(request, "users/index_shell.html", {"shell_list": shell_list})
@ -852,7 +1258,17 @@ def index_shell(request):
@login_required
@can_view_all(ListRight)
def index_listright(request):
""" Affiche l'ensemble des droits"""
"""View for displaying the listrights/groups list in re2o.
The listrights are sorted by members users, and individual
acl for a complete display.
Parameters:
request (django request): Standard django request.
Returns:
Django ListRight Form.
"""
rights = {}
for right in (
ListRight.objects.order_by("name")
@ -875,7 +1291,17 @@ def index_listright(request):
@login_required
@can_view_all(ServiceUser)
def index_serviceusers(request):
""" Affiche les users de services (pour les accès ldap)"""
"""View for displaying the paginated list of all serviceusers in re2o
See ServiceUser model for more informations on service users.
Need the global acl for viewing all serviceusers, can_view_all.
Parameters:
request (django request): Standard django request.
Returns:
Django ServiceUser Form.
"""
serviceusers_list = ServiceUser.objects.order_by("pseudo")
return render(
request,
@ -886,14 +1312,42 @@ def index_serviceusers(request):
@login_required
def mon_profil(request):
""" Lien vers profil, renvoie request.id à la fonction """
"""Shortcuts view to profil view, with correct arguments.
Returns the view profil with users argument, users is set to
default request.user.
Parameters:
request (django request): Standard django request.
Returns:
Django User Profil Form.
"""
return redirect(reverse("users:profil", kwargs={"userid": str(request.user.id)}))
@login_required
@can_view(User)
def profil(request, users, **_kwargs):
""" Affiche un profil, self or cableur, prend un userid en argument """
"""Profil view. Display informations on users, the single user.
Informations displayed are:
* Adherent or Club User instance informations
* Interface/Machine belonging to User instance
* Invoice belonging to User instance
* Ban instances belonging to User
* Whitelists instances belonging to User
* Email Settings of User instance
* Tickets belonging to User instance.
Requires the acl can_view on user instance.
Parameters:
request (django request): Standard django request.
users: User instance to display profil
Returns:
Django User Profil Form.
"""
machines = (
Machine.objects.filter(user=users)
.select_related("user")
@ -969,7 +1423,17 @@ def profil(request, users, **_kwargs):
def reset_password(request):
""" Reintialisation du mot de passe si mdp oublié """
"""Reset password form, linked to form forgotten password.
If an user is found, send an email to him with a link
to reset its password.
Parameters:
request (django request): Standard django request.
Returns:
Django ResetPassword Form.
"""
userform = ResetPasswordForm(request.POST or None)
if userform.is_valid():
try:
@ -994,8 +1458,17 @@ def reset_password(request):
def process(request, token):
"""Process, lien pour la reinitialisation du mot de passe
et la confirmation de l'email"""
"""Process view, in case of both reset password, or confirm email in case
of new email set.
This view calls process_passwd or process_email.
Parameters:
request (django request): Standard django request.
Returns:
Correct Django process Form.
"""
valid_reqs = Request.objects.filter(expires_at__gt=timezone.now())
req = get_object_or_404(valid_reqs, token=token)
@ -1009,8 +1482,16 @@ def process(request, token):
def process_passwd(request, req):
"""Process le changeemnt de mot de passe, renvoie le formulaire
demandant le nouveau password"""
"""Process view, in case of reset password by email. Returns
a form to change and reset the password.
Parameters:
request (django request): Standard django request.
Returns:
Correct Django process password Form.
"""
user = req.user
u_form = PassForm(request.POST or None, instance=user, user=request.user)
if u_form.is_valid():
@ -1031,8 +1512,17 @@ def process_passwd(request, req):
def process_email(request, req):
"""Process la confirmation de mail, renvoie le formulaire
de validation"""
"""Process view, in case of confirm a new email. Returns
a form to notify the success of the email confirmation to
request.User.
Parameters:
request (django request): Standard django request.
Returns:
Correct Django process email Form.
"""
user = req.user
if request.method == "POST":
with transaction.atomic(), reversion.create_revision():
@ -1055,7 +1545,16 @@ def process_email(request, req):
@login_required
@can_edit(User)
def resend_confirmation_email(request, logged_user, userid):
""" Renvoi du mail de confirmation """
"""View to resend confirm email, for adding a new email.
Check if User has the correct acl.
Parameters:
request (django request): Standard django request.
Returns:
Correct Django resend email Form.
"""
try:
user = User.objects.get(
id=userid,
@ -1074,6 +1573,20 @@ def resend_confirmation_email(request, logged_user, userid):
@login_required
def initial_register(request):
"""View to register both a new room, and a new interface/machine for a user.
This view is used with switchs function of redirect web after AAA authentication
failed. Then, the users log-in, and the new mac-address and switch port, in order to
get the room, are included in HTTP Headers by the switch redirection functionnality.
This allow to add the new interface with the correct mac-address, and confirm if needed,
the new room of request.user.
Parameters:
request (django request): Standard django request.
Returns:
Initial room and interface/machine register Form.
"""
switch_ip = request.GET.get("switch_ip", None)
switch_port = request.GET.get("switch_port", None)
client_mac = request.GET.get("client_mac", None)