8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-25 12:53:11 +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 FactureAdmin(VersionAdmin):
"""Class admin d'une facture, tous les champs""" """Admin class for invoices."""
pass pass
@ -52,32 +52,31 @@ class CustomInvoiceAdmin(VersionAdmin):
class VenteAdmin(VersionAdmin): class VenteAdmin(VersionAdmin):
"""Class admin d'une vente, tous les champs (facture related)""" """Admin class for purchases."""
pass pass
class ArticleAdmin(VersionAdmin): class ArticleAdmin(VersionAdmin):
"""Class admin d'un article en vente""" """Admin class for articles."""
pass pass
class BanqueAdmin(VersionAdmin): class BanqueAdmin(VersionAdmin):
"""Class admin de la liste des banques (facture related)""" """Admin class for banks."""
pass pass
class PaiementAdmin(VersionAdmin): class PaiementAdmin(VersionAdmin):
"""Class admin d'un moyen de paiement (facture related""" """Admin class for payment methods."""
pass pass
class CotisationAdmin(VersionAdmin): class CotisationAdmin(VersionAdmin):
"""Class admin d'une cotisation (date de debut et de fin), """Admin class for subscriptions."""
Vente related"""
pass pass

View file

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

View file

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

View file

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

View file

@ -19,9 +19,8 @@
# You should have received a copy of the GNU General Public License along # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """logs.urls
Urls de l'application logs, pointe vers les fonctions de views. The defined URLs for the logs app. Included in re2o.urls.
Inclu dans le re2o.urls
""" """
from __future__ import unicode_literals from __future__ import unicode_literals

View file

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

View file

@ -22,15 +22,15 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
Formulaires d'ajout, edition et suppressions de : Forms to create, edit and delete:
- machines * machines
- interfaces * interfaces
- domain (noms de machine) * domains (machine names)
- alias (cname) * aliases (CNAME)
- service (dhcp, dns..) * services (DHCP, DNS...)
- ns (serveur dns) * NS records (DNS server)
- mx (serveur mail) * MX records (mail serveur)
- ports ouverts et profils d'ouverture par interface * open ports and ports opening for interfaces
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
@ -66,7 +66,7 @@ from .models import (
class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Formulaire d'édition d'une machine""" """Form used to edit a machine."""
class Meta: class Meta:
model = Machine model = Machine
@ -79,14 +79,14 @@ class EditMachineForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class NewMachineForm(EditMachineForm): class NewMachineForm(EditMachineForm):
"""Creation d'une machine, ne renseigne que le nom""" """Form used to create a machine."""
class Meta(EditMachineForm.Meta): class Meta(EditMachineForm.Meta):
fields = ["name"] fields = ["name"]
class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Edition d'une interface. Edition complète""" """Form used to edit an interface."""
class Meta: class Meta:
model = Interface model = Interface
@ -127,15 +127,14 @@ class EditInterfaceForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class AddInterfaceForm(EditInterfaceForm): class AddInterfaceForm(EditInterfaceForm):
"""Ajout d'une interface à une machine. En fonction des droits, """Form used to add an interface to a machine."""
affiche ou non l'ensemble des ip disponibles"""
class Meta(EditInterfaceForm.Meta): class Meta(EditInterfaceForm.Meta):
fields = ["machine_type", "ipv4", "mac_address", "details"] fields = ["machine_type", "ipv4", "mac_address", "details"]
class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): 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: class Meta:
model = Domain model = Domain
@ -153,7 +152,9 @@ class AliasForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class DomainForm(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: class Meta:
model = Domain model = Domain
@ -165,7 +166,7 @@ class DomainForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class DelAliasForm(FormRevMixin, Form): class DelAliasForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs objets alias""" """Form used to delete one or several aliases."""
alias = forms.ModelMultipleChoiceField( alias = forms.ModelMultipleChoiceField(
queryset=Domain.objects.all(), queryset=Domain.objects.all(),
@ -182,7 +183,7 @@ class DelAliasForm(FormRevMixin, Form):
class MachineTypeForm(FormRevMixin, ModelForm): 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: class Meta:
model = MachineType model = MachineType
@ -196,7 +197,7 @@ class MachineTypeForm(FormRevMixin, ModelForm):
class DelMachineTypeForm(FormRevMixin, Form): class DelMachineTypeForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs machinetype""" """Form used to delete one or several machines types."""
machinetypes = forms.ModelMultipleChoiceField( machinetypes = forms.ModelMultipleChoiceField(
queryset=MachineType.objects.none(), queryset=MachineType.objects.none(),
@ -214,8 +215,9 @@ class DelMachineTypeForm(FormRevMixin, Form):
class IpTypeForm(FormRevMixin, ModelForm): class IpTypeForm(FormRevMixin, ModelForm):
"""Formulaire d'ajout d'un iptype. Pas d'edition de l'ip de start et de """Form used to add an IP type. The start and stop IP addresses cannot
stop après creation""" be changed afterwards.
"""
class Meta: class Meta:
model = IpType model = IpType
@ -228,8 +230,9 @@ class IpTypeForm(FormRevMixin, ModelForm):
class EditIpTypeForm(IpTypeForm): class EditIpTypeForm(IpTypeForm):
"""Edition d'un iptype. Pas d'edition du rangev4 possible, car il faudrait """Form used to edit an IP type. The start and stop IP addresses cannot
synchroniser les objets iplist""" be changed.
"""
class Meta(IpTypeForm.Meta): class Meta(IpTypeForm.Meta):
fields = [ fields = [
@ -248,7 +251,7 @@ class EditIpTypeForm(IpTypeForm):
class DelIpTypeForm(FormRevMixin, Form): class DelIpTypeForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs iptype""" """Form used to delete one or several IP types."""
iptypes = forms.ModelMultipleChoiceField( iptypes = forms.ModelMultipleChoiceField(
queryset=IpType.objects.none(), queryset=IpType.objects.none(),
@ -266,7 +269,7 @@ class DelIpTypeForm(FormRevMixin, Form):
class ExtensionForm(FormRevMixin, ModelForm): class ExtensionForm(FormRevMixin, ModelForm):
"""Formulaire d'ajout et edition d'une extension""" """Form used to add and edit extensions."""
class Meta: class Meta:
model = Extension model = Extension
@ -283,7 +286,7 @@ class ExtensionForm(FormRevMixin, ModelForm):
class DelExtensionForm(FormRevMixin, Form): class DelExtensionForm(FormRevMixin, Form):
"""Suppression d'une ou plusieurs extensions""" """Form used to delete one or several extensions."""
extensions = forms.ModelMultipleChoiceField( extensions = forms.ModelMultipleChoiceField(
queryset=Extension.objects.none(), queryset=Extension.objects.none(),
@ -301,7 +304,7 @@ class DelExtensionForm(FormRevMixin, Form):
class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm): class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
"""Gestion des ipv6 d'une machine""" """Form used to manage lists of IPv6 addresses."""
class Meta: class Meta:
model = Ipv6List model = Ipv6List
@ -313,7 +316,7 @@ class Ipv6ListForm(FormRevMixin, FieldPermissionFormMixin, ModelForm):
class SOAForm(FormRevMixin, ModelForm): class SOAForm(FormRevMixin, ModelForm):
"""Ajout et edition d'un SOA""" """Form used to add and edit SOA records."""
class Meta: class Meta:
model = SOA model = SOA
@ -325,7 +328,7 @@ class SOAForm(FormRevMixin, ModelForm):
class DelSOAForm(FormRevMixin, Form): class DelSOAForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs SOA""" """Form used to delete one or several SOA records."""
soa = forms.ModelMultipleChoiceField( soa = forms.ModelMultipleChoiceField(
queryset=SOA.objects.none(), queryset=SOA.objects.none(),
@ -343,7 +346,7 @@ class DelSOAForm(FormRevMixin, Form):
class MxForm(FormRevMixin, ModelForm): class MxForm(FormRevMixin, ModelForm):
"""Ajout et edition d'un MX""" """Form used to add and edit MX records."""
class Meta: class Meta:
model = Mx model = Mx
@ -358,7 +361,7 @@ class MxForm(FormRevMixin, ModelForm):
class DelMxForm(FormRevMixin, Form): class DelMxForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs MX""" """Form used to delete one or several MX records."""
mx = forms.ModelMultipleChoiceField( mx = forms.ModelMultipleChoiceField(
queryset=Mx.objects.none(), queryset=Mx.objects.none(),
@ -376,9 +379,9 @@ class DelMxForm(FormRevMixin, Form):
class NsForm(FormRevMixin, ModelForm): class NsForm(FormRevMixin, ModelForm):
"""Ajout d'un NS pour une zone """Form used to add and edit NS records. Only interface names are
On exclue les CNAME dans les objets domain (interdit par la rfc) available because CNAME aliases should not be used in the records.
donc on prend uniquemet """ """
class Meta: class Meta:
model = Ns model = Ns
@ -393,7 +396,7 @@ class NsForm(FormRevMixin, ModelForm):
class DelNsForm(FormRevMixin, Form): class DelNsForm(FormRevMixin, Form):
"""Suppresion d'un ou plusieurs NS""" """Form used to delete one or several NS records."""
nss = forms.ModelMultipleChoiceField( nss = forms.ModelMultipleChoiceField(
queryset=Ns.objects.none(), queryset=Ns.objects.none(),
@ -411,7 +414,7 @@ class DelNsForm(FormRevMixin, Form):
class TxtForm(FormRevMixin, ModelForm): class TxtForm(FormRevMixin, ModelForm):
"""Ajout d'un txt pour une zone""" """Form used to add and edit TXT records."""
class Meta: class Meta:
model = Txt model = Txt
@ -423,7 +426,7 @@ class TxtForm(FormRevMixin, ModelForm):
class DelTxtForm(FormRevMixin, Form): class DelTxtForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs TXT""" """Form used to delete one or several TXT records."""
txt = forms.ModelMultipleChoiceField( txt = forms.ModelMultipleChoiceField(
queryset=Txt.objects.none(), queryset=Txt.objects.none(),
@ -441,7 +444,7 @@ class DelTxtForm(FormRevMixin, Form):
class DNameForm(FormRevMixin, ModelForm): class DNameForm(FormRevMixin, ModelForm):
"""Add a DNAME entry for a zone""" """Form used to add and edit DNAME records."""
class Meta: class Meta:
model = DName model = DName
@ -453,7 +456,7 @@ class DNameForm(FormRevMixin, ModelForm):
class DelDNameForm(FormRevMixin, Form): class DelDNameForm(FormRevMixin, Form):
"""Delete a set of DNAME entries""" """Form used to delete one or several DNAME records."""
dnames = forms.ModelMultipleChoiceField( dnames = forms.ModelMultipleChoiceField(
queryset=Txt.objects.none(), queryset=Txt.objects.none(),
@ -471,7 +474,7 @@ class DelDNameForm(FormRevMixin, Form):
class SrvForm(FormRevMixin, ModelForm): class SrvForm(FormRevMixin, ModelForm):
"""Ajout d'un srv pour une zone""" """Form used to add and edit SRV records."""
class Meta: class Meta:
model = Srv model = Srv
@ -483,7 +486,7 @@ class SrvForm(FormRevMixin, ModelForm):
class DelSrvForm(FormRevMixin, Form): class DelSrvForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs Srv""" """Form used to delete one or several SRV records."""
srv = forms.ModelMultipleChoiceField( srv = forms.ModelMultipleChoiceField(
queryset=Srv.objects.none(), queryset=Srv.objects.none(),
@ -501,8 +504,7 @@ class DelSrvForm(FormRevMixin, Form):
class NasForm(FormRevMixin, ModelForm): class NasForm(FormRevMixin, ModelForm):
"""Ajout d'un type de nas (machine d'authentification, """Form used to create and edit NAS devices."""
swicths, bornes...)"""
class Meta: class Meta:
model = Nas model = Nas
@ -514,7 +516,7 @@ class NasForm(FormRevMixin, ModelForm):
class DelNasForm(FormRevMixin, Form): class DelNasForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs nas""" """Form used to delete one or several NAS devices."""
nas = forms.ModelMultipleChoiceField( nas = forms.ModelMultipleChoiceField(
queryset=Nas.objects.none(), queryset=Nas.objects.none(),
@ -532,7 +534,7 @@ class DelNasForm(FormRevMixin, Form):
class RoleForm(FormRevMixin, ModelForm): class RoleForm(FormRevMixin, ModelForm):
"""Add and edit role.""" """Form used to add and edit roles."""
class Meta: class Meta:
model = Role model = Role
@ -547,7 +549,7 @@ class RoleForm(FormRevMixin, ModelForm):
class DelRoleForm(FormRevMixin, Form): class DelRoleForm(FormRevMixin, Form):
"""Deletion of one or several roles.""" """Form used to delete one or several roles."""
role = forms.ModelMultipleChoiceField( role = forms.ModelMultipleChoiceField(
queryset=Role.objects.none(), queryset=Role.objects.none(),
@ -565,7 +567,7 @@ class DelRoleForm(FormRevMixin, Form):
class ServiceForm(FormRevMixin, ModelForm): 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: class Meta:
model = Service model = Service
@ -589,7 +591,7 @@ class ServiceForm(FormRevMixin, ModelForm):
class DelServiceForm(FormRevMixin, Form): class DelServiceForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs service""" """Form used to delete one or several services."""
service = forms.ModelMultipleChoiceField( service = forms.ModelMultipleChoiceField(
queryset=Service.objects.none(), queryset=Service.objects.none(),
@ -607,7 +609,7 @@ class DelServiceForm(FormRevMixin, Form):
class VlanForm(FormRevMixin, ModelForm): class VlanForm(FormRevMixin, ModelForm):
"""Ajout d'un vlan : id, nom""" """Form used to add and edit VLANs."""
class Meta: class Meta:
model = Vlan model = Vlan
@ -619,7 +621,7 @@ class VlanForm(FormRevMixin, ModelForm):
class EditOptionVlanForm(FormRevMixin, ModelForm): class EditOptionVlanForm(FormRevMixin, ModelForm):
"""Ajout d'un vlan : id, nom""" """Form used to edit the options of a VLAN."""
class Meta: class Meta:
model = Vlan model = Vlan
@ -631,7 +633,7 @@ class EditOptionVlanForm(FormRevMixin, ModelForm):
class DelVlanForm(FormRevMixin, Form): class DelVlanForm(FormRevMixin, Form):
"""Suppression d'un ou plusieurs vlans""" """Form used to delete one or several VLANs."""
vlan = forms.ModelMultipleChoiceField( vlan = forms.ModelMultipleChoiceField(
queryset=Vlan.objects.none(), queryset=Vlan.objects.none(),
@ -649,8 +651,7 @@ class DelVlanForm(FormRevMixin, Form):
class EditOuverturePortConfigForm(FormRevMixin, ModelForm): class EditOuverturePortConfigForm(FormRevMixin, ModelForm):
"""Edition de la liste des profils d'ouverture de ports """Form to edit the ports opening list of an interface."""
pour l'interface"""
class Meta: class Meta:
model = Interface model = Interface
@ -664,8 +665,7 @@ class EditOuverturePortConfigForm(FormRevMixin, ModelForm):
class EditOuverturePortListForm(FormRevMixin, ModelForm): class EditOuverturePortListForm(FormRevMixin, ModelForm):
"""Edition de la liste des ports et profils d'ouverture """Form used to add and edit ports opening lists."""
des ports"""
class Meta: class Meta:
model = OuverturePortList model = OuverturePortList
@ -677,7 +677,7 @@ class EditOuverturePortListForm(FormRevMixin, ModelForm):
class SshFpForm(FormRevMixin, ModelForm): class SshFpForm(FormRevMixin, ModelForm):
"""Edits a SSHFP record.""" """Form used to add and edit SSHFP records."""
class Meta: class Meta:
model = SshFp 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_create(Machine)
@can_edit(User) @can_edit(User)
def new_machine(request, user, **_kwargs): def new_machine(request, user, **_kwargs):
""" Fonction de creation d'une machine. Cree l'objet machine, """View used to create machines.
le sous objet interface et l'objet domain à partir de model forms.
Trop complexe, devrait être simplifié"""
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) machine = NewMachineForm(request.POST or None, user=request.user)
interface = AddInterfaceForm(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()}) 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 @login_required
@can_edit(Interface) @can_edit(Interface)
def edit_interface(request, interface_instance, **_kwargs): def edit_interface(request, interface_instance, **_kwargs):
""" Edition d'une interface. Distingue suivant les droits les valeurs """View used to edit interfaces.
de interfaces et machines que l'user peut modifier infra permet de
modifier le propriétaire"""
The values a user can change depends on their rights.
"""
machine_form = EditMachineForm( machine_form = EditMachineForm(
request.POST or None, instance=interface_instance.machine, user=request.user request.POST or None, instance=interface_instance.machine, user=request.user
) )
@ -295,7 +296,7 @@ def edit_interface(request, interface_instance, **_kwargs):
@login_required @login_required
@can_delete(Machine) @can_delete(Machine)
def del_machine(request, machine, **_kwargs): 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": if request.method == "POST":
machine.delete() machine.delete()
messages.success(request, _("The machine was deleted.")) messages.success(request, _("The machine was deleted."))
@ -311,8 +312,9 @@ def del_machine(request, machine, **_kwargs):
@can_create(Interface) @can_create(Interface)
@can_edit(Machine) @can_edit(Machine)
def new_interface(request, machine, **_kwargs): 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) 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()}) domain_form = DomainForm(request.POST or None, user=request.user, initial={'name': machine.user.get_next_domain_name()})
if interface_form.is_valid(): if interface_form.is_valid():
@ -344,7 +346,7 @@ def new_interface(request, machine, **_kwargs):
@login_required @login_required
@can_delete(Interface) @can_delete(Interface)
def del_interface(request, interface, **_kwargs): 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": if request.method == "POST":
machine = interface.machine machine = interface.machine
interface.delete() interface.delete()
@ -363,7 +365,7 @@ def del_interface(request, interface, **_kwargs):
@can_create(Ipv6List) @can_create(Ipv6List)
@can_edit(Interface) @can_edit(Interface)
def new_ipv6list(request, interface, **_kwargs): def new_ipv6list(request, interface, **_kwargs):
"""Nouvelle ipv6""" """View used to create IPv6 addresses lists."""
ipv6list_instance = Ipv6List(interface=interface) ipv6list_instance = Ipv6List(interface=interface)
ipv6 = Ipv6ListForm( ipv6 = Ipv6ListForm(
request.POST or None, instance=ipv6list_instance, user=request.user request.POST or None, instance=ipv6list_instance, user=request.user
@ -384,7 +386,7 @@ def new_ipv6list(request, interface, **_kwargs):
@login_required @login_required
@can_edit(Ipv6List) @can_edit(Ipv6List)
def edit_ipv6list(request, ipv6list_instance, **_kwargs): def edit_ipv6list(request, ipv6list_instance, **_kwargs):
"""Edition d'une ipv6""" """View used to edit IPv6 addresses lists."""
ipv6 = Ipv6ListForm( ipv6 = Ipv6ListForm(
request.POST or None, instance=ipv6list_instance, user=request.user request.POST or None, instance=ipv6list_instance, user=request.user
) )
@ -406,7 +408,7 @@ def edit_ipv6list(request, ipv6list_instance, **_kwargs):
@login_required @login_required
@can_delete(Ipv6List) @can_delete(Ipv6List)
def del_ipv6list(request, ipv6list, **_kwargs): def del_ipv6list(request, ipv6list, **_kwargs):
""" Supprime une ipv6""" """View used to delete IPv6 addresses lists."""
if request.method == "POST": if request.method == "POST":
interfaceid = ipv6list.interface.id interfaceid = ipv6list.interface.id
ipv6list.delete() ipv6list.delete()
@ -423,7 +425,7 @@ def del_ipv6list(request, ipv6list, **_kwargs):
@can_create(SshFp) @can_create(SshFp)
@can_edit(Machine) @can_edit(Machine)
def new_sshfp(request, machine, **_kwargs): 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_instance = SshFp(machine=machine)
sshfp = SshFpForm(request.POST or None, instance=sshfp_instance) sshfp = SshFpForm(request.POST or None, instance=sshfp_instance)
if sshfp.is_valid(): if sshfp.is_valid():
@ -442,7 +444,7 @@ def new_sshfp(request, machine, **_kwargs):
@login_required @login_required
@can_edit(SshFp) @can_edit(SshFp)
def edit_sshfp(request, sshfp_instance, **_kwargs): 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) sshfp = SshFpForm(request.POST or None, instance=sshfp_instance)
if sshfp.is_valid(): if sshfp.is_valid():
if sshfp.changed_data: if sshfp.changed_data:
@ -462,7 +464,7 @@ def edit_sshfp(request, sshfp_instance, **_kwargs):
@login_required @login_required
@can_delete(SshFp) @can_delete(SshFp)
def del_sshfp(request, sshfp, **_kwargs): def del_sshfp(request, sshfp, **_kwargs):
"""Deletes an SSHFP record""" """View used to delete SSHFP records."""
if request.method == "POST": if request.method == "POST":
machineid = sshfp.machine.id machineid = sshfp.machine.id
sshfp.delete() sshfp.delete()
@ -478,9 +480,10 @@ def del_sshfp(request, sshfp, **_kwargs):
@login_required @login_required
@can_create(IpType) @can_create(IpType)
def add_iptype(request): def add_iptype(request):
""" Ajoute un range d'ip. Intelligence dans le models, fonction views """View used to create IP ranges.
minimaliste"""
The view function is simple, the intelligence is in the model.
"""
iptype = IpTypeForm(request.POST or None) iptype = IpTypeForm(request.POST or None)
if iptype.is_valid(): if iptype.is_valid():
iptype.save() iptype.save()
@ -496,9 +499,10 @@ def add_iptype(request):
@login_required @login_required
@can_edit(IpType) @can_edit(IpType)
def edit_iptype(request, iptype_instance, **_kwargs): def edit_iptype(request, iptype_instance, **_kwargs):
""" Edition d'un range. Ne permet pas de le redimensionner pour éviter """View used to edit IP ranges.
l'incohérence"""
Changing the size of the range is not possible to prevent inconsistency.
"""
iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance) iptype = EditIpTypeForm(request.POST or None, instance=iptype_instance)
if iptype.is_valid(): if iptype.is_valid():
if iptype.changed_data: if iptype.changed_data:
@ -515,7 +519,10 @@ def edit_iptype(request, iptype_instance, **_kwargs):
@login_required @login_required
@can_delete_set(IpType) @can_delete_set(IpType)
def del_iptype(request, instances): 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) iptype = DelIpTypeForm(request.POST or None, instances=instances)
if iptype.is_valid(): if iptype.is_valid():
iptype_dels = iptype.cleaned_data["iptypes"] iptype_dels = iptype.cleaned_data["iptypes"]
@ -545,7 +552,7 @@ def del_iptype(request, instances):
@login_required @login_required
@can_create(MachineType) @can_create(MachineType)
def add_machinetype(request): def add_machinetype(request):
""" View used to add a Machinetype object """ """View used to create machine types."""
machinetype = MachineTypeForm(request.POST or None) machinetype = MachineTypeForm(request.POST or None)
if machinetype.is_valid(): if machinetype.is_valid():
machinetype.save() machinetype.save()
@ -561,7 +568,7 @@ def add_machinetype(request):
@login_required @login_required
@can_edit(MachineType) @can_edit(MachineType)
def edit_machinetype(request, machinetype_instance, **_kwargs): 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) machinetype = MachineTypeForm(request.POST or None, instance=machinetype_instance)
if machinetype.is_valid(): if machinetype.is_valid():
if machinetype.changed_data: if machinetype.changed_data:
@ -578,7 +585,7 @@ def edit_machinetype(request, machinetype_instance, **_kwargs):
@login_required @login_required
@can_delete_set(MachineType) @can_delete_set(MachineType)
def del_machinetype(request, instances): 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) machinetype = DelMachineTypeForm(request.POST or None, instances=instances)
if machinetype.is_valid(): if machinetype.is_valid():
machinetype_dels = machinetype.cleaned_data["machinetypes"] machinetype_dels = machinetype.cleaned_data["machinetypes"]
@ -608,7 +615,7 @@ def del_machinetype(request, instances):
@login_required @login_required
@can_create(Extension) @can_create(Extension)
def add_extension(request): def add_extension(request):
""" View used to add an Extension object """ """View used to create extensions."""
extension = ExtensionForm(request.POST or None) extension = ExtensionForm(request.POST or None)
if extension.is_valid(): if extension.is_valid():
extension.save() extension.save()
@ -624,7 +631,7 @@ def add_extension(request):
@login_required @login_required
@can_edit(Extension) @can_edit(Extension)
def edit_extension(request, extension_instance, **_kwargs): 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) extension = ExtensionForm(request.POST or None, instance=extension_instance)
if extension.is_valid(): if extension.is_valid():
if extension.changed_data: if extension.changed_data:
@ -641,7 +648,7 @@ def edit_extension(request, extension_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Extension) @can_delete_set(Extension)
def del_extension(request, instances): 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) extension = DelExtensionForm(request.POST or None, instances=instances)
if extension.is_valid(): if extension.is_valid():
extension_dels = extension.cleaned_data["extensions"] extension_dels = extension.cleaned_data["extensions"]
@ -670,7 +677,7 @@ def del_extension(request, instances):
@login_required @login_required
@can_create(SOA) @can_create(SOA)
def add_soa(request): def add_soa(request):
""" View used to add a SOA object """ """View used to create SOA records."""
soa = SOAForm(request.POST or None) soa = SOAForm(request.POST or None)
if soa.is_valid(): if soa.is_valid():
soa.save() soa.save()
@ -686,7 +693,7 @@ def add_soa(request):
@login_required @login_required
@can_edit(SOA) @can_edit(SOA)
def edit_soa(request, soa_instance, **_kwargs): 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) soa = SOAForm(request.POST or None, instance=soa_instance)
if soa.is_valid(): if soa.is_valid():
if soa.changed_data: if soa.changed_data:
@ -701,7 +708,7 @@ def edit_soa(request, soa_instance, **_kwargs):
@login_required @login_required
@can_delete_set(SOA) @can_delete_set(SOA)
def del_soa(request, instances): 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) soa = DelSOAForm(request.POST or None, instances=instances)
if soa.is_valid(): if soa.is_valid():
soa_dels = soa.cleaned_data["soa"] soa_dels = soa.cleaned_data["soa"]
@ -722,7 +729,7 @@ def del_soa(request, instances):
@login_required @login_required
@can_create(Mx) @can_create(Mx)
def add_mx(request): def add_mx(request):
""" View used to add a MX object """ """View used to create MX records."""
mx = MxForm(request.POST or None) mx = MxForm(request.POST or None)
if mx.is_valid(): if mx.is_valid():
mx.save() mx.save()
@ -738,7 +745,7 @@ def add_mx(request):
@login_required @login_required
@can_edit(Mx) @can_edit(Mx)
def edit_mx(request, mx_instance, **_kwargs): 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) mx = MxForm(request.POST or None, instance=mx_instance)
if mx.is_valid(): if mx.is_valid():
if mx.changed_data: if mx.changed_data:
@ -753,7 +760,7 @@ def edit_mx(request, mx_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Mx) @can_delete_set(Mx)
def del_mx(request, instances): 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) mx = DelMxForm(request.POST or None, instances=instances)
if mx.is_valid(): if mx.is_valid():
mx_dels = mx.cleaned_data["mx"] mx_dels = mx.cleaned_data["mx"]
@ -774,7 +781,7 @@ def del_mx(request, instances):
@login_required @login_required
@can_create(Ns) @can_create(Ns)
def add_ns(request): def add_ns(request):
""" View used to add a NS object """ """View used to create NS records."""
ns = NsForm(request.POST or None) ns = NsForm(request.POST or None)
if ns.is_valid(): if ns.is_valid():
ns.save() ns.save()
@ -790,7 +797,7 @@ def add_ns(request):
@login_required @login_required
@can_edit(Ns) @can_edit(Ns)
def edit_ns(request, ns_instance, **_kwargs): 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) ns = NsForm(request.POST or None, instance=ns_instance)
if ns.is_valid(): if ns.is_valid():
if ns.changed_data: if ns.changed_data:
@ -805,7 +812,7 @@ def edit_ns(request, ns_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Ns) @can_delete_set(Ns)
def del_ns(request, instances): 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) nss = DelNsForm(request.POST or None, instances=instances)
if nss.is_valid(): if nss.is_valid():
ns_dels = nss.cleaned_data["nss"] ns_dels = nss.cleaned_data["nss"]
@ -826,7 +833,7 @@ def del_ns(request, instances):
@login_required @login_required
@can_create(DName) @can_create(DName)
def add_dname(request): def add_dname(request):
""" View used to add a DName object """ """View used to create DNAME records."""
dname = DNameForm(request.POST or None) dname = DNameForm(request.POST or None)
if dname.is_valid(): if dname.is_valid():
dname.save() dname.save()
@ -842,7 +849,7 @@ def add_dname(request):
@login_required @login_required
@can_edit(DName) @can_edit(DName)
def edit_dname(request, dname_instance, **_kwargs): 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) dname = DNameForm(request.POST or None, instance=dname_instance)
if dname.is_valid(): if dname.is_valid():
if dname.changed_data: if dname.changed_data:
@ -857,7 +864,7 @@ def edit_dname(request, dname_instance, **_kwargs):
@login_required @login_required
@can_delete_set(DName) @can_delete_set(DName)
def del_dname(request, instances): 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) dname = DelDNameForm(request.POST or None, instances=instances)
if dname.is_valid(): if dname.is_valid():
dname_dels = dname.cleaned_data["dname"] dname_dels = dname.cleaned_data["dname"]
@ -881,7 +888,7 @@ def del_dname(request, instances):
@login_required @login_required
@can_create(Txt) @can_create(Txt)
def add_txt(request): def add_txt(request):
""" View used to add a TXT object """ """View used to create TXT records."""
txt = TxtForm(request.POST or None) txt = TxtForm(request.POST or None)
if txt.is_valid(): if txt.is_valid():
txt.save() txt.save()
@ -897,7 +904,7 @@ def add_txt(request):
@login_required @login_required
@can_edit(Txt) @can_edit(Txt)
def edit_txt(request, txt_instance, **_kwargs): 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) txt = TxtForm(request.POST or None, instance=txt_instance)
if txt.is_valid(): if txt.is_valid():
if txt.changed_data: if txt.changed_data:
@ -912,7 +919,7 @@ def edit_txt(request, txt_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Txt) @can_delete_set(Txt)
def del_txt(request, instances): 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) txt = DelTxtForm(request.POST or None, instances=instances)
if txt.is_valid(): if txt.is_valid():
txt_dels = txt.cleaned_data["txt"] txt_dels = txt.cleaned_data["txt"]
@ -933,7 +940,7 @@ def del_txt(request, instances):
@login_required @login_required
@can_create(Srv) @can_create(Srv)
def add_srv(request): def add_srv(request):
""" View used to add a SRV object """ """View used to create SRV records."""
srv = SrvForm(request.POST or None) srv = SrvForm(request.POST or None)
if srv.is_valid(): if srv.is_valid():
srv.save() srv.save()
@ -949,7 +956,7 @@ def add_srv(request):
@login_required @login_required
@can_edit(Srv) @can_edit(Srv)
def edit_srv(request, srv_instance, **_kwargs): 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) srv = SrvForm(request.POST or None, instance=srv_instance)
if srv.is_valid(): if srv.is_valid():
if srv.changed_data: if srv.changed_data:
@ -964,7 +971,7 @@ def edit_srv(request, srv_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Srv) @can_delete_set(Srv)
def del_srv(request, instances): 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) srv = DelSrvForm(request.POST or None, instances=instances)
if srv.is_valid(): if srv.is_valid():
srv_dels = srv.cleaned_data["srv"] srv_dels = srv.cleaned_data["srv"]
@ -986,7 +993,7 @@ def del_srv(request, instances):
@can_create(Domain) @can_create(Domain)
@can_edit(Interface) @can_edit(Interface)
def add_alias(request, interface, interfaceid): 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) alias = AliasForm(request.POST or None, user=request.user)
if alias.is_valid(): if alias.is_valid():
alias = alias.save(commit=False) alias = alias.save(commit=False)
@ -1006,7 +1013,7 @@ def add_alias(request, interface, interfaceid):
@login_required @login_required
@can_edit(Domain) @can_edit(Domain)
def edit_alias(request, domain_instance, **_kwargs): 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) alias = AliasForm(request.POST or None, instance=domain_instance, user=request.user)
if alias.is_valid(): if alias.is_valid():
if alias.changed_data: if alias.changed_data:
@ -1026,7 +1033,7 @@ def edit_alias(request, domain_instance, **_kwargs):
@login_required @login_required
@can_edit(Interface) @can_edit(Interface)
def del_alias(request, interface, interfaceid): 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) alias = DelAliasForm(request.POST or None, interface=interface)
if alias.is_valid(): if alias.is_valid():
alias_dels = alias.cleaned_data["alias"] alias_dels = alias.cleaned_data["alias"]
@ -1051,7 +1058,7 @@ def del_alias(request, interface, interfaceid):
@login_required @login_required
@can_create(Role) @can_create(Role)
def add_role(request): def add_role(request):
""" View used to add a Role object """ """View used to create roles."""
role = RoleForm(request.POST or None) role = RoleForm(request.POST or None)
if role.is_valid(): if role.is_valid():
role.save() role.save()
@ -1067,7 +1074,7 @@ def add_role(request):
@login_required @login_required
@can_edit(Role) @can_edit(Role)
def edit_role(request, role_instance, **_kwargs): 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) role = RoleForm(request.POST or None, instance=role_instance)
if role.is_valid(): if role.is_valid():
if role.changed_data: if role.changed_data:
@ -1082,7 +1089,7 @@ def edit_role(request, role_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Role) @can_delete_set(Role)
def del_role(request, instances): 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) role = DelRoleForm(request.POST or None, instances=instances)
if role.is_valid(): if role.is_valid():
role_dels = role.cleaned_data["role"] role_dels = role.cleaned_data["role"]
@ -1103,7 +1110,7 @@ def del_role(request, instances):
@login_required @login_required
@can_create(Service) @can_create(Service)
def add_service(request): def add_service(request):
""" View used to add a Service object """ """View used to create services."""
service = ServiceForm(request.POST or None) service = ServiceForm(request.POST or None)
if service.is_valid(): if service.is_valid():
service.save() service.save()
@ -1119,7 +1126,7 @@ def add_service(request):
@login_required @login_required
@can_edit(Service) @can_edit(Service)
def edit_service(request, service_instance, **_kwargs): 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) service = ServiceForm(request.POST or None, instance=service_instance)
if service.is_valid(): if service.is_valid():
if service.changed_data: if service.changed_data:
@ -1136,7 +1143,7 @@ def edit_service(request, service_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Service) @can_delete_set(Service)
def del_service(request, instances): 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) service = DelServiceForm(request.POST or None, instances=instances)
if service.is_valid(): if service.is_valid():
service_dels = service.cleaned_data["service"] service_dels = service.cleaned_data["service"]
@ -1169,7 +1176,7 @@ def regen_service(request, service, **_kwargs):
@login_required @login_required
@can_create(Vlan) @can_create(Vlan)
def add_vlan(request): def add_vlan(request):
""" View used to add a VLAN object """ """View used to create VLANs."""
vlan = VlanForm(request.POST or None) vlan = VlanForm(request.POST or None)
if vlan.is_valid(): if vlan.is_valid():
vlan.save() vlan.save()
@ -1185,7 +1192,7 @@ def add_vlan(request):
@login_required @login_required
@can_edit(Vlan) @can_edit(Vlan)
def edit_vlan(request, vlan_instance, **_kwargs): 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) vlan = VlanForm(request.POST or None, instance=vlan_instance)
if vlan.is_valid(): if vlan.is_valid():
if vlan.changed_data: if vlan.changed_data:
@ -1200,7 +1207,7 @@ def edit_vlan(request, vlan_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Vlan) @can_delete_set(Vlan)
def del_vlan(request, instances): 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) vlan = DelVlanForm(request.POST or None, instances=instances)
if vlan.is_valid(): if vlan.is_valid():
vlan_dels = vlan.cleaned_data["vlan"] vlan_dels = vlan.cleaned_data["vlan"]
@ -1221,7 +1228,7 @@ def del_vlan(request, instances):
@login_required @login_required
@can_create(Nas) @can_create(Nas)
def add_nas(request): def add_nas(request):
""" View used to add a NAS object """ """View used to create NAS devices."""
nas = NasForm(request.POST or None) nas = NasForm(request.POST or None)
if nas.is_valid(): if nas.is_valid():
nas.save() nas.save()
@ -1237,7 +1244,7 @@ def add_nas(request):
@login_required @login_required
@can_edit(Nas) @can_edit(Nas)
def edit_nas(request, nas_instance, **_kwargs): 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) nas = NasForm(request.POST or None, instance=nas_instance)
if nas.is_valid(): if nas.is_valid():
if nas.changed_data: if nas.changed_data:
@ -1252,7 +1259,7 @@ def edit_nas(request, nas_instance, **_kwargs):
@login_required @login_required
@can_delete_set(Nas) @can_delete_set(Nas)
def del_nas(request, instances): 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) nas = DelNasForm(request.POST or None, instances=instances)
if nas.is_valid(): if nas.is_valid():
nas_dels = nas.cleaned_data["nas"] nas_dels = nas.cleaned_data["nas"]
@ -1273,8 +1280,8 @@ def del_nas(request, instances):
@login_required @login_required
@can_view_all(Machine) @can_view_all(Machine)
def index(request): def index(request):
""" The home view for this app. Displays the list of registered """The home view for this app. Displays the list of registered
machines in Re2o """ machines in Re2o."""
pagination_large_number = GeneralOption.get_cached_value("pagination_large_number") pagination_large_number = GeneralOption.get_cached_value("pagination_large_number")
machines_list = ( machines_list = (
Machine.objects.select_related("user") Machine.objects.select_related("user")
@ -1297,7 +1304,7 @@ def index(request):
@login_required @login_required
@can_view_all(IpType) @can_view_all(IpType)
def index_iptype(request): 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_list = (
IpType.objects.select_related("extension") IpType.objects.select_related("extension")
.select_related("vlan") .select_related("vlan")
@ -1309,7 +1316,7 @@ def index_iptype(request):
@login_required @login_required
@can_view_all(Vlan) @can_view_all(Vlan)
def index_vlan(request): 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") vlan_list = Vlan.objects.prefetch_related("iptype_set").order_by("vlan_id")
return render(request, "machines/index_vlan.html", {"vlan_list": vlan_list}) return render(request, "machines/index_vlan.html", {"vlan_list": vlan_list})
@ -1317,7 +1324,7 @@ def index_vlan(request):
@login_required @login_required
@can_view_all(MachineType) @can_view_all(MachineType)
def index_machinetype(request): 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") machinetype_list = MachineType.objects.select_related("ip_type").order_by("name")
return render( return render(
request, request,
@ -1329,7 +1336,7 @@ def index_machinetype(request):
@login_required @login_required
@can_view_all(Nas) @can_view_all(Nas)
def index_nas(request): def index_nas(request):
""" View displaying the list of existing NAS """ """View used to display the list of existing NAS devices."""
nas_list = ( nas_list = (
Nas.objects.select_related("machine_type") Nas.objects.select_related("machine_type")
.select_related("nas_type") .select_related("nas_type")
@ -1341,10 +1348,11 @@ def index_nas(request):
@login_required @login_required
@can_view_all(SOA, Mx, Ns, Txt, DName, Srv, Extension) @can_view_all(SOA, Mx, Ns, Txt, DName, Srv, Extension)
def index_extension(request): 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 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 NS records, the list of existing TXT records and the list of
existing SRV records """ existing SRV records.
"""
extension_list = ( extension_list = (
Extension.objects.select_related("origin") Extension.objects.select_related("origin")
.select_related("soa") .select_related("soa")
@ -1386,7 +1394,7 @@ def index_extension(request):
@login_required @login_required
@can_edit(Interface) @can_edit(Interface)
def index_alias(request, interface, interfaceid): 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( alias_list = Domain.objects.filter(
cname=Domain.objects.filter(interface_parent=interface) cname=Domain.objects.filter(interface_parent=interface)
).order_by("name") ).order_by("name")
@ -1401,7 +1409,7 @@ def index_alias(request, interface, interfaceid):
@can_view(Machine) @can_view(Machine)
def index_sshfp(request, machine, machineid): def index_sshfp(request, machine, machineid):
"""View used to display the list of existing SSHFP records associated """View used to display the list of existing SSHFP records associated
with a machine""" with a machine."""
sshfp_list = SshFp.objects.filter(machine=machine) sshfp_list = SshFp.objects.filter(machine=machine)
return render( return render(
request, request,
@ -1413,7 +1421,7 @@ def index_sshfp(request, machine, machineid):
@login_required @login_required
@can_view(Interface) @can_view(Interface)
def index_ipv6(request, interface, interfaceid): 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) ipv6_list = Ipv6List.objects.filter(interface=interface)
return render( return render(
request, request,
@ -1425,7 +1433,7 @@ def index_ipv6(request, interface, interfaceid):
@login_required @login_required
@can_view_all(Role) @can_view_all(Role)
def index_role(request): 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() role_list = Role.objects.prefetch_related("servers__domain__extension").all()
return render(request, "machines/index_role.html", {"role_list": role_list}) return render(request, "machines/index_role.html", {"role_list": role_list})
@ -1433,7 +1441,7 @@ def index_role(request):
@login_required @login_required
@can_view_all(Service) @can_view_all(Service)
def index_service(request): 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_list = Service.objects.prefetch_related(
"service_link_set__server__domain__extension" "service_link_set__server__domain__extension"
).all() ).all()
@ -1452,7 +1460,7 @@ def index_service(request):
@login_required @login_required
@can_view_all(OuverturePortList) @can_view_all(OuverturePortList)
def index_portlist(request): 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 = ( port_list = (
OuverturePortList.objects.prefetch_related("ouvertureport_set") OuverturePortList.objects.prefetch_related("ouvertureport_set")
.prefetch_related("interface_set__domain__extension") .prefetch_related("interface_set__domain__extension")
@ -1465,7 +1473,7 @@ def index_portlist(request):
@login_required @login_required
@can_edit(OuverturePortList) @can_edit(OuverturePortList)
def edit_portlist(request, ouvertureportlist_instance, **_kwargs): def edit_portlist(request, ouvertureportlist_instance, **_kwargs):
""" View used to edit a port policy """ """View used to edit port policies."""
port_list = EditOuverturePortListForm( port_list = EditOuverturePortListForm(
request.POST or None, instance=ouvertureportlist_instance request.POST or None, instance=ouvertureportlist_instance
) )
@ -1500,7 +1508,7 @@ def edit_portlist(request, ouvertureportlist_instance, **_kwargs):
@login_required @login_required
@can_delete(OuverturePortList) @can_delete(OuverturePortList)
def del_portlist(request, port_list_instance, **_kwargs): 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() port_list_instance.delete()
messages.success(request, _("The ports list was deleted.")) messages.success(request, _("The ports list was deleted."))
return redirect(reverse("machines:index-portlist")) return redirect(reverse("machines:index-portlist"))
@ -1509,7 +1517,7 @@ def del_portlist(request, port_list_instance, **_kwargs):
@login_required @login_required
@can_create(OuverturePortList) @can_create(OuverturePortList)
def add_portlist(request): 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_list = EditOuverturePortListForm(request.POST or None)
port_formset = modelformset_factory( port_formset = modelformset_factory(
OuverturePort, OuverturePort,
@ -1540,8 +1548,9 @@ def add_portlist(request):
@can_create(OuverturePort) @can_create(OuverturePort)
@can_edit(Interface) @can_edit(Interface)
def configure_ports(request, interface_instance, **_kwargs): def configure_ports(request, interface_instance, **_kwargs):
""" View to display the list of configured port policy for an """View to display the list of configured port policies for an
interface """ interface.
"""
if not interface_instance.may_have_port_open(): if not interface_instance.may_have_port_open():
messages.error( messages.error(
request, request,

View file

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

View file

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

View file

@ -19,7 +19,8 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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): class Preferences(models.Model):
""" Definition of the app settings""" """Definition of the settings of multi_op."""
enabled_dorm = models.ManyToManyField( enabled_dorm = models.ManyToManyField(
"topologie.Dormitory", "topologie.Dormitory",

View file

@ -53,7 +53,13 @@ from .preferences.forms import EditPreferencesForm
def display_rooms_connection(request, dormitory=None): 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( room_list = Room.objects.select_related("building__dormitory").order_by(
"building_dormitory", "port" "building_dormitory", "port"
) )
@ -81,19 +87,30 @@ def display_rooms_connection(request, dormitory=None):
@login_required @login_required
@can_view_all(Room) @can_view_all(Room)
def aff_state_global(request): def aff_state_global(request):
"""View used to display the connection state of all rooms."""
return display_rooms_connection(request) return display_rooms_connection(request)
@login_required @login_required
@can_view(Dormitory) @can_view(Dormitory)
def aff_state_dormitory(request, dormitory, dormitoryid): 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) return display_rooms_connection(dormitory=dormitory)
@login_required @login_required
@can_view_all(Room) @can_view_all(Room)
def aff_pending_connection(request): 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_list = (
Room.objects.select_related("building__dormitory") Room.objects.select_related("building__dormitory")
.filter(port__isnull=True) .filter(port__isnull=True)
@ -128,7 +145,9 @@ def aff_pending_connection(request):
@login_required @login_required
@can_view_all(Room) @can_view_all(Room)
def aff_pending_disconnection(request): 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_list = (
Room.objects.select_related("building__dormitory") Room.objects.select_related("building__dormitory")
.filter(port__isnull=False) .filter(port__isnull=False)
@ -163,7 +182,13 @@ def aff_pending_disconnection(request):
@login_required @login_required
@can_edit(Room) @can_edit(Room)
def disconnect_room(request, room, roomid): 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.port_set.clear()
room.save() room.save()
messages.success(request, _("The room %s was disconnected.") % room) messages.success(request, _("The room %s was disconnected.") % room)
@ -171,5 +196,7 @@ def disconnect_room(request, room, roomid):
def navbar_user(): 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")) 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 from __future__ import unicode_literals
@ -46,79 +46,79 @@ from .models import (
class OptionalUserAdmin(VersionAdmin): class OptionalUserAdmin(VersionAdmin):
"""Class admin options user""" """Admin class for user options."""
pass pass
class OptionalTopologieAdmin(VersionAdmin): class OptionalTopologieAdmin(VersionAdmin):
"""Class admin options topologie""" """Admin class for topology options."""
pass pass
class OptionalMachineAdmin(VersionAdmin): class OptionalMachineAdmin(VersionAdmin):
"""Class admin options machines""" """Admin class for machines options."""
pass pass
class GeneralOptionAdmin(VersionAdmin): class GeneralOptionAdmin(VersionAdmin):
"""Class admin options générales""" """Admin class for general options."""
pass pass
class ServiceAdmin(VersionAdmin): class ServiceAdmin(VersionAdmin):
"""Class admin gestion des services de la page d'accueil""" """Admin class for services (on the homepage)."""
pass pass
class MailContactAdmin(VersionAdmin): class MailContactAdmin(VersionAdmin):
"""Admin class for contact email adresses""" """Admin class for contact email addresses."""
pass pass
class AssoOptionAdmin(VersionAdmin): class AssoOptionAdmin(VersionAdmin):
"""Class admin options de l'asso""" """Admin class for organisation options."""
pass pass
class MailMessageOptionAdmin(VersionAdmin): class MailMessageOptionAdmin(VersionAdmin):
"""Class admin options mail""" """Admin class for email messages options."""
pass pass
class HomeOptionAdmin(VersionAdmin): class HomeOptionAdmin(VersionAdmin):
"""Class admin options home""" """Admin class for home options."""
pass pass
class RadiusKeyAdmin(VersionAdmin): class RadiusKeyAdmin(VersionAdmin):
"""Class radiuskey""" """Admin class for RADIUS keys options."""
pass pass
class SwitchManagementCredAdmin(VersionAdmin): class SwitchManagementCredAdmin(VersionAdmin):
"""Class managementcred for switch""" """Admin class for switch management credentials options."""
pass pass
class ReminderAdmin(VersionAdmin): class ReminderAdmin(VersionAdmin):
"""Class reminder for switch""" """Admin class for reminder options."""
pass pass
class DocumentTemplateAdmin(VersionAdmin): class DocumentTemplateAdmin(VersionAdmin):
"""Admin class for DocumentTemplate""" """Admin class for document templates."""
pass pass

View file

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

View file

@ -21,7 +21,8 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 from __future__ import unicode_literals
import os import os
@ -44,20 +45,22 @@ from datetime import timedelta
class PreferencesModel(models.Model): class PreferencesModel(models.Model):
""" Base object for the Preferences objects """Base object for the Preferences objects.
Defines methods to handle the cache of the settings (they should
not change a lot) """ Defines methods to handle the cache of the settings (they should not change
a lot).
"""
@classmethod @classmethod
def set_in_cache(cls): 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() instance, _created = cls.objects.get_or_create()
cache.set(cls().__class__.__name__.lower(), instance, None) cache.set(cls().__class__.__name__.lower(), instance, None)
return instance return instance
@classmethod @classmethod
def get_cached_value(cls, key): 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()) instance = cache.get(cls().__class__.__name__.lower())
if instance is None: if instance is None:
instance = cls.set_in_cache() instance = cls.set_in_cache()
@ -68,8 +71,34 @@ class PreferencesModel(models.Model):
class OptionalUser(AclMixin, PreferencesModel): class OptionalUser(AclMixin, PreferencesModel):
"""Options pour l'user : obligation ou nom du telephone, """User preferences: telephone number requirement, user balance activation,
activation ou non du solde, autorisation du negatif, fingerprint etc""" 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" DISABLED = "DISABLED"
ONLY_INACTIVE = "ONLY_INACTIVE" ONLY_INACTIVE = "ONLY_INACTIVE"
@ -158,23 +187,32 @@ class OptionalUser(AclMixin, PreferencesModel):
verbose_name = _("user preferences") verbose_name = _("user preferences")
def clean(self): def clean(self):
"""Clean model: """Check the email extension."""
Check the mail_extension
"""
if self.local_email_domain[0] != "@": if self.local_email_domain[0] != "@":
raise ValidationError(_("Email domain must begin with @.")) raise ValidationError(_("Email domain must begin with @."))
@receiver(post_save, sender=OptionalUser) @receiver(post_save, sender=OptionalUser)
def optionaluser_post_save(**kwargs): def optionaluser_post_save(**kwargs):
"""Ecriture dans le cache""" """Write in the cache."""
user_pref = kwargs["instance"] user_pref = kwargs["instance"]
user_pref.set_in_cache() user_pref.set_in_cache()
class OptionalMachine(AclMixin, PreferencesModel): class OptionalMachine(AclMixin, PreferencesModel):
"""Options pour les machines : maximum de machines ou d'alias par user """Machines preferences: maximum number of machines per user, IPv6
sans droit, activation de l'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" SLAAC = "SLAAC"
DHCPV6 = "DHCPV6" DHCPV6 = "DHCPV6"
@ -197,7 +235,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@cached_property @cached_property
def ipv6(self): 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" return not self.get_cached_value("ipv6_mode") == "DISABLED"
class Meta: class Meta:
@ -207,7 +245,7 @@ class OptionalMachine(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalMachine) @receiver(post_save, sender=OptionalMachine)
def optionalmachine_post_save(**kwargs): 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 = kwargs["instance"]
machine_pref.set_in_cache() machine_pref.set_in_cache()
if machine_pref.ipv6_mode != "DISABLED": if machine_pref.ipv6_mode != "DISABLED":
@ -216,8 +254,21 @@ def optionalmachine_post_save(**kwargs):
class OptionalTopologie(AclMixin, PreferencesModel): class OptionalTopologie(AclMixin, PreferencesModel):
"""Reglages pour la topologie : mode d'accès radius, vlan où placer """Configuration of switches: automatic provision, RADIUS mode, default
les machines en accept ou reject""" 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" MACHINE = "MACHINE"
DEFINED = "DEFINED" DEFINED = "DEFINED"
@ -264,7 +315,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property @cached_property
def provisioned_switchs(self): def provisioned_switchs(self):
"""Liste des switches provisionnés""" """Get the list of provisioned switches."""
from topologie.models import Switch from topologie.models import Switch
return Switch.objects.filter(automatic_provision=True).order_by( return Switch.objects.filter(automatic_provision=True).order_by(
@ -273,7 +324,9 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property @cached_property
def switchs_management_interface(self): 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: if self.switchs_ip_type:
from machines.models import Role, Interface from machines.models import Role, Interface
@ -291,14 +344,16 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property @cached_property
def switchs_management_interface_ip(self): 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: if not self.switchs_management_interface:
return None return None
return self.switchs_management_interface.ipv4 return self.switchs_management_interface.ipv4
@cached_property @cached_property
def switchs_management_sftp_creds(self): 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: if self.sftp_login and self.sftp_pass:
return {"login": self.sftp_login, "pass": self.sftp_pass} return {"login": self.sftp_login, "pass": self.sftp_pass}
else: else:
@ -306,7 +361,9 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property @cached_property
def switchs_management_utils(self): 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 from machines.models import Role, Ipv6List, Interface
def return_ips_dict(interfaces): def return_ips_dict(interfaces):
@ -350,8 +407,7 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@cached_property @cached_property
def provision_switchs_enabled(self): def provision_switchs_enabled(self):
"""Return true if all settings are ok : switchs on automatic provision, """Check if all automatic provisioning settings are OK."""
ip_type"""
return bool( return bool(
self.provisioned_switchs self.provisioned_switchs
and self.switchs_ip_type and self.switchs_ip_type
@ -371,13 +427,20 @@ class OptionalTopologie(AclMixin, PreferencesModel):
@receiver(post_save, sender=OptionalTopologie) @receiver(post_save, sender=OptionalTopologie)
def optionaltopologie_post_save(**kwargs): def optionaltopologie_post_save(**kwargs):
"""Ecriture dans le cache""" """Write in the cache."""
topologie_pref = kwargs["instance"] topologie_pref = kwargs["instance"]
topologie_pref.set_in_cache() topologie_pref.set_in_cache()
class RadiusKey(AclMixin, models.Model): 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.")) radius_key = AESEncryptedField(max_length=255, help_text=_("RADIUS key."))
comment = models.CharField( comment = models.CharField(
@ -393,9 +456,7 @@ class RadiusKey(AclMixin, models.Model):
verbose_name_plural = _("RADIUS keys") verbose_name_plural = _("RADIUS keys")
def clean(self): def clean(self):
"""Clean model: """Check if there is a unique default RADIUS key."""
Check default switch is unique
"""
if RadiusKey.objects.filter(default_switch=True).count() > 1: if RadiusKey.objects.filter(default_switch=True).count() > 1:
raise ValidationError(_("Default RADIUS key for switches already exists.")) raise ValidationError(_("Default RADIUS key for switches already exists."))
@ -404,7 +465,14 @@ class RadiusKey(AclMixin, models.Model):
class SwitchManagementCred(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_id = models.CharField(max_length=63, help_text=_("Switch login."))
management_pass = AESEncryptedField(max_length=63, help_text=_("Password.")) management_pass = AESEncryptedField(max_length=63, help_text=_("Password."))
@ -426,9 +494,13 @@ class SwitchManagementCred(AclMixin, models.Model):
class Reminder(AclMixin, models.Model): class Reminder(AclMixin, models.Model):
"""Options pour les mails de notification de fin d'adhésion. """Reminder of membership's end preferences: email messages, number of days
Days: liste des nombres de jours pour lesquells un mail est envoyé before sending emails.
optionalMessage: message additionel pour le mail
Attributes:
days: the number of days before the membership's end to send the
reminder.
message: the content of the reminder.
""" """
days = models.IntegerField( days = models.IntegerField(
@ -460,8 +532,26 @@ class Reminder(AclMixin, models.Model):
class GeneralOption(AclMixin, PreferencesModel): class GeneralOption(AclMixin, PreferencesModel):
"""Options générales : nombre de resultats par page, nom du site, """General preferences: number of search results per page, website name
temps les liens sont valides""" 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( general_message_fr = models.TextField(
default="", default="",
@ -496,14 +586,20 @@ class GeneralOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=GeneralOption) @receiver(post_save, sender=GeneralOption)
def generaloption_post_save(**kwargs): def generaloption_post_save(**kwargs):
"""Ecriture dans le cache""" """Write in the cache."""
general_pref = kwargs["instance"] general_pref = kwargs["instance"]
general_pref.set_in_cache() general_pref.set_in_cache()
class Service(AclMixin, models.Model): class Service(AclMixin, models.Model):
"""Liste des services affichés sur la page d'accueil : url, description, """Service displayed on the home page.
image et nom"""
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) name = models.CharField(max_length=32)
url = models.URLField() url = models.URLField()
@ -520,7 +616,12 @@ class Service(AclMixin, models.Model):
class MailContact(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( address = models.EmailField(
default="contact@example.org", help_text=_("Contact email address.") default="contact@example.org", help_text=_("Contact email address.")
@ -549,6 +650,15 @@ class MailContact(AclMixin, models.Model):
class Mandate(RevMixin, 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: class Meta:
verbose_name = _("mandate") verbose_name = _("mandate")
verbose_name_plural = _("mandates") verbose_name_plural = _("mandates")
@ -567,7 +677,14 @@ class Mandate(RevMixin, AclMixin, models.Model):
@classmethod @classmethod
def get_mandate(cls, date=timezone.now): 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): if callable(date):
date = date() date = date()
mandate = ( mandate = (
@ -590,7 +707,21 @@ class Mandate(RevMixin, AclMixin, models.Model):
class AssoOption(AclMixin, PreferencesModel): 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( name = models.CharField(
default=_("Networking organisation school Something"), max_length=256 default=_("Networking organisation school Something"), max_length=256
@ -613,13 +744,20 @@ class AssoOption(AclMixin, PreferencesModel):
@receiver(post_save, sender=AssoOption) @receiver(post_save, sender=AssoOption)
def assooption_post_save(**kwargs): def assooption_post_save(**kwargs):
"""Ecriture dans le cache""" """Write in the cache."""
asso_pref = kwargs["instance"] asso_pref = kwargs["instance"]
asso_pref.set_in_cache() asso_pref.set_in_cache()
class HomeOption(AclMixin, PreferencesModel): 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) facebook_url = models.URLField(null=True, blank=True)
twitter_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) @receiver(post_save, sender=HomeOption)
def homeoption_post_save(**kwargs): def homeoption_post_save(**kwargs):
"""Ecriture dans le cache""" """Write in the cache."""
home_pref = kwargs["instance"] home_pref = kwargs["instance"]
home_pref.set_in_cache() home_pref.set_in_cache()
class MailMessageOption(AclMixin, models.Model): 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( welcome_mail_fr = models.TextField(
default="", blank=True, help_text=_("Welcome email in French.") default="", blank=True, help_text=_("Welcome email in French.")
@ -655,6 +798,14 @@ class MailMessageOption(AclMixin, models.Model):
class RadiusAttribute(RevMixin, 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: class Meta:
verbose_name = _("RADIUS attribute") verbose_name = _("RADIUS attribute")
verbose_name_plural = _("RADIUS attributes") verbose_name_plural = _("RADIUS attributes")
@ -677,6 +828,30 @@ class RadiusAttribute(RevMixin, AclMixin, models.Model):
class RadiusOption(AclMixin, PreferencesModel): 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: class Meta:
verbose_name = _("RADIUS policy") verbose_name = _("RADIUS policy")
verbose_name_plural = _("RADIUS policies") verbose_name_plural = _("RADIUS policies")
@ -847,6 +1022,15 @@ def default_voucher():
class CotisationsOption(AclMixin, PreferencesModel): 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: class Meta:
verbose_name = _("subscription preferences") verbose_name = _("subscription preferences")
@ -877,6 +1061,10 @@ class CotisationsOption(AclMixin, PreferencesModel):
class DocumentTemplate(RevMixin, AclMixin, models.Model): class DocumentTemplate(RevMixin, AclMixin, models.Model):
"""Represent a template in order to create documents such as invoice or """Represent a template in order to create documents such as invoice or
subscription voucher. 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")) 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) @receiver(models.signals.post_delete, sender=DocumentTemplate)
def auto_delete_file_on_delete(sender, instance, **kwargs): def auto_delete_file_on_delete(sender, instance, **kwargs):
""" """Delete the tempalte file from filesystem when the related
Deletes file from filesystem DocumentTemplate object is deleted.
when corresponding `DocumentTemplate` object is deleted.
""" """
if instance.template: if instance.template:
if os.path.isfile(instance.template.path): 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) @receiver(models.signals.pre_save, sender=DocumentTemplate)
def auto_delete_file_on_change(sender, instance, **kwargs): def auto_delete_file_on_change(sender, instance, **kwargs):
""" """Delete the previous file from filesystem when the related
Deletes old file from filesystem DocumentTemplate object is updated with new file.
when corresponding `DocumentTemplate` object is updated
with new file.
""" """
if not instance.pk: if not instance.pk:
return False return False

View file

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

View file

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

View file

@ -19,7 +19,7 @@
# You should have received a copy of the GNU General Public License along # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 from __future__ import unicode_literals
@ -34,8 +34,12 @@ from re2o.settings_local import OPTIONNAL_APPS_RE2O
def context_user(request): def context_user(request):
"""Fonction de context lorsqu'un user est logué (ou non), """Global Context function
renvoie les infos sur l'user, la liste de ses droits, ses machines"""
Returns:
dict:Containing user's interfaces and himself if logged, else None
"""
user = request.user user = request.user
if get_language() == "fr": if get_language() == "fr":
global_message = GeneralOption.get_cached_value("general_message_fr") global_message = GeneralOption.get_cached_value("general_message_fr")
@ -61,8 +65,13 @@ def context_user(request):
def context_optionnal_apps(request): def context_optionnal_apps(request):
"""Fonction de context pour générer la navbar en fonction des """Context functions. Called to add optionnal apps buttons in navbari
apps optionnels"""
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_apps = [import_module(app) for app in OPTIONNAL_APPS_RE2O]
optionnal_templates_navbar_user_list = [ optionnal_templates_navbar_user_list = [
app.views.navbar_user() app.views.navbar_user()

View file

@ -85,7 +85,13 @@ class FieldPermissionModelMixin:
class FieldPermissionFormMixin: 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): def __init__(self, *args, **kwargs):

View file

@ -45,7 +45,14 @@ DIGEST_LEN = 20
def makeSecret(password): 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) salt = os.urandom(4)
h = hashlib.sha1(password.encode()) h = hashlib.sha1(password.encode())
h.update(salt) h.update(salt)
@ -53,13 +60,30 @@ def makeSecret(password):
def hashNT(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() hash_str = hashlib.new("md4", password.encode("utf-16le")).digest()
return binascii.hexlify(hash_str).upper() return binascii.hexlify(hash_str).upper()
def checkPassword(challenge_password, password): 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()) challenge_bytes = decodestring(challenge_password[ALGO_LEN:].encode())
digest = challenge_bytes[:DIGEST_LEN] digest = challenge_bytes[:DIGEST_LEN]
salt = challenge_bytes[DIGEST_LEN:] salt = challenge_bytes[DIGEST_LEN:]
@ -69,7 +93,15 @@ def checkPassword(challenge_password, password):
def hash_password_salt(hashed_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}"): if hashed_password.upper().startswith("{CRYPT}"):
hashed_password = hashed_password[7:] hashed_password = hashed_password[7:]
if hashed_password.startswith("$"): if hashed_password.startswith("$"):
@ -243,6 +275,14 @@ class SSHAPasswordHasher(hashers.BasePasswordHasher):
class RecryptBackend(ModelBackend): 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): def authenticate(self, username=None, password=None):
# we obtain from the classical auth backend the user # we obtain from the classical auth backend the user
user = super(RecryptBackend, self).authenticate(None, username, password) user = super(RecryptBackend, self).authenticate(None, username, password)

View file

@ -22,7 +22,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Jean-Romain Garnier # 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 _ from django.utils.translation import ugettext_lazy as _

View file

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

View file

@ -47,7 +47,15 @@ application = get_wsgi_application()
def get_user(pseudo): 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) user = User.objects.filter(pseudo=pseudo)
if len(user) == 0: if len(user) == 0:
raise CommandError("Invalid user.") raise CommandError("Invalid user.")
@ -59,17 +67,20 @@ def get_user(pseudo):
def get_system_user(): 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 return pwd.getpwuid(int(os.getenv("SUDO_UID") or os.getuid())).pw_name
def form_cli(Form, user, action, *args, **kwargs): def form_cli(Form, user, action, *args, **kwargs):
""" """
Remplit un formulaire à partir de la ligne de commande Fill-in a django form from cli
Form : le formulaire (sous forme de classe) à remplir
user : l'utilisateur re2o faisant la modification Parameters
action : l'action réalisée par le formulaire (pour les logs) Form : a django class form to fill-in
Les arguments suivants sont transmis tels quels au formulaire. user : a re2o user doign the modification
action: the action done with that form, for logs purpose
""" """
data = {} data = {}
dumb_form = Form(user=user, *args, **kwargs) dumb_form = Form(user=user, *args, **kwargs)

View file

@ -24,12 +24,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# David Sinquin, Gabriel Détraz, Lara Kermarec # David Sinquin, Gabriel Détraz, Lara Kermarec
""" """
Regroupe les fonctions transversales utiles A group of very usefull functions for re2o core
Fonction : Functions:
- récupérer tous les utilisateurs actifs - find all active users
- récupérer toutes les machines - find all active interfaces
- récupérer tous les bans - find all bans
etc etc
""" """
@ -47,7 +47,14 @@ from preferences.models import AssoOption
def get_group_having_permission(*permission_name): 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() groups = set()
for name in permission_name: 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): def all_adherent(search_time=None, including_asso=True):
""" Fonction renvoyant tous les users adherents. Optimisee pour n'est """Return all people who have a valid membership at org. Optimised to make only one
qu'une seule requete sql sql query. Build a filter and then apply it to User. Check for each user if a valid
Inspecte les factures de l'user et ses cotisation, regarde si elles membership is registered at the desired search_time.
sont posterieur à now (end_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: if search_time is None:
search_time = timezone.now() search_time = timezone.now()
filter_user = Q( filter_user = Q(
@ -86,7 +102,18 @@ def all_adherent(search_time=None, including_asso=True):
def all_baned(search_time=None): 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: if search_time is None:
search_time = timezone.now() search_time = timezone.now()
return User.objects.filter( return User.objects.filter(
@ -97,7 +124,18 @@ def all_baned(search_time=None):
def all_whitelisted(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: if search_time is None:
search_time = timezone.now() search_time = timezone.now()
return User.objects.filter( return User.objects.filter(
@ -108,11 +146,19 @@ def all_whitelisted(search_time=None):
def all_has_access(search_time=None, including_asso=True): def all_has_access(search_time=None, including_asso=True):
""" Return all connected users : active users and whitelisted + """Return all people who have an valid internet access at org. Optimised to make
asso_user defined in AssoOption pannel 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.
Renvoie tous les users beneficiant d'une connexion
: user adherent et whiteliste non banni plus l'utilisateur asso""" 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: if search_time is None:
search_time = timezone.now() search_time = timezone.now()
filter_user = ( filter_user = (
@ -153,7 +199,20 @@ def all_has_access(search_time=None, including_asso=True):
def filter_active_interfaces(interface_set): 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 ( return (
interface_set.filter( interface_set.filter(
machine__in=Machine.objects.filter(user__in=all_has_access()).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): 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") return filter_active_interfaces(interface_set).prefetch_related("ipv6list")
def all_active_interfaces(full=False): 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: if full:
return filter_complete_interfaces(Interface.objects) return filter_complete_interfaces(Interface.objects)
else: else:
@ -184,13 +269,30 @@ def all_active_interfaces(full=False):
def all_active_assigned_interfaces(full=False): def all_active_assigned_interfaces(full=False):
""" Renvoie l'ensemble des machines qui ont une ipv4 assignées et """Return all interfaces of people who have an valid internet access at org,
disposant de l'accès internet""" 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) return all_active_interfaces(full=full).filter(ipv4__isnull=False)
def all_active_interfaces_count(): 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( return Interface.objects.filter(
machine__in=Machine.objects.filter(user__in=all_has_access()).filter( machine__in=Machine.objects.filter(user__in=all_has_access()).filter(
active=True active=True
@ -199,12 +301,26 @@ def all_active_interfaces_count():
def all_active_assigned_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) return all_active_interfaces_count().filter(ipv4__isnull=False)
def remove_user_room(room, force=True): 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: try:
user = Adherent.objects.get(room=room) user = Adherent.objects.get(room=room)
except Adherent.DoesNotExist: except Adherent.DoesNotExist:

View file

@ -21,8 +21,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
Fonctions de la page d'accueil et diverses fonctions utiles pour tous Welcom main page view, and several template widely used in re2o views
les views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
@ -50,14 +49,29 @@ from re2o.settings_local import OPTIONNAL_APPS_RE2O
def form(ctx, template, request): 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 = ctx
context.update(csrf(request)) context.update(csrf(request))
return render(request, template, context) return render(request, template, context)
def index(request): 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 = [[], [], []] services = [[], [], []]
for indice, serv in enumerate(Service.objects.all()): for indice, serv in enumerate(Service.objects.all()):
services[indice % 3].append(serv) services[indice % 3].append(serv)

View file

@ -45,21 +45,43 @@ from re2o.base import SortTable, re2o_paginator
class Query: class Query:
"""Class representing a query. """Class representing a query.
It can contain the user-entered text, the operator for the 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): def __init__(self, text="", case_sensitive=False):
self.text = text # Content of the query """Initialise an instance of Query.
self.operator = None # Whether a special char (ex "+") was used
self.subqueries = None # When splitting the query in subparts 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 self.case_sensitive = case_sensitive
def add_char(self, char): 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 self.text += char
def add_operator(self, operator): def add_operator(self, operator):
"""Consider a new operator was entered, and that it must be processed. """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 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 self.operator = operator
if self.subqueries is None: if self.subqueries is None:
@ -71,7 +93,7 @@ class Query:
@property @property
def plaintext(self): 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: if self.operator is not None:
return self.operator.join([q.plaintext for q in self.subqueries]) return self.operator.join([q.plaintext for q in self.subqueries])
@ -82,7 +104,7 @@ class Query:
def filter_fields(): def filter_fields():
"""Return the list of fields the search applies to""" """Return the list of fields the search applies to."""
return ["users", return ["users",
"clubs", "clubs",
"machines", "machines",
@ -95,12 +117,12 @@ def filter_fields():
def empty_filters(): def empty_filters():
"""Build empty filters used by Django""" """Build empty filters used by Django."""
return {f: Q() for f in filter_fields()} return {f: Q() for f in filter_fields()}
def is_int(variable): 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: try:
int(variable) int(variable)
except ValueError: except ValueError:
@ -111,8 +133,18 @@ def is_int(variable):
def finish_results(request, results, col, order): def finish_results(request, results, col, order):
"""Sort the results by applying filters and then limit them to the """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 number of max results. Finally add the info of the maximum number of
to the dict""" 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"] = SortTable.sort(
results["users"], col, order, SortTable.USERS_INDEX 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): def contains_filter(attribute, word, case_sensitive=False):
"""Create a django model filtering whether the given attribute """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: if case_sensitive:
attr = "{}__{}".format(attribute, "contains") attr = "{}__{}".format(attribute, "contains")
else: else:
@ -168,12 +209,13 @@ def contains_filter(attribute, word, case_sensitive=False):
def search_single_word(word, filters, user, start, end, def search_single_word(word, filters, user, start, end,
user_state, email_state, aff, user_state, email_state, aff,
case_sensitive=False): 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. 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 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 query). IntegerField are matched against the query only if it can be casted
to an int.""" to an int.
"""
# Users # Users
if "0" in aff: if "0" in aff:
@ -333,7 +375,7 @@ def search_single_word(word, filters, user, start, end,
def apply_filters(filters, user, aff): 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 It also takes into account the visual filters defined during
the search query. 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): def search_single_query(query, filters, user, start, end, user_state, email_state, aff):
""" Handle different queries an construct the correct filters using """Handle different queries an construct the correct filters using
search_single_word""" search_single_word."""
if query.operator == "+": if query.operator == "+":
# Special queries with "+" operators should use & rather than | # Special queries with "+" operators should use & rather than |
newfilters = empty_filters() newfilters = empty_filters()

View file

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

View file

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

View file

@ -35,7 +35,7 @@ from .models import Ticket, CommentTicket
class NewTicketForm(FormRevMixin, ModelForm): class NewTicketForm(FormRevMixin, ModelForm):
""" Creation of a ticket""" """Form used to create tickets."""
class Meta: class Meta:
model = Ticket model = Ticket
@ -53,7 +53,7 @@ class NewTicketForm(FormRevMixin, ModelForm):
class EditTicketForm(FormRevMixin, ModelForm): class EditTicketForm(FormRevMixin, ModelForm):
""" Creation of a ticket""" """Form used to edit tickets."""
class Meta: class Meta:
model = Ticket model = Ticket
@ -65,7 +65,7 @@ class EditTicketForm(FormRevMixin, ModelForm):
class CommentTicketForm(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: class Meta:
model = CommentTicket model = CommentTicket

View file

@ -46,7 +46,23 @@ from .preferences.models import TicketOption
class Ticket(AclMixin, models.Model): 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( user = models.ForeignKey(
"users.User", "users.User",
@ -86,7 +102,7 @@ class Ticket(AclMixin, models.Model):
@cached_property @cached_property
def opened_by(self): 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: if self.user:
return self.user.get_full_name() return self.user.get_full_name()
else: else:
@ -94,10 +110,13 @@ class Ticket(AclMixin, models.Model):
@cached_property @cached_property
def get_mail(self): 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 return self.email or self.user.get_mail
def publish_mail(self): 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") site_url = GeneralOption.get_cached_value("main_site_url")
to_addr = TicketOption.get_cached_value("publish_address") to_addr = TicketOption.get_cached_value("publish_address")
context = {"ticket": self, "site_url": site_url} context = {"ticket": self, "site_url": site_url}
@ -120,8 +139,8 @@ class Ticket(AclMixin, models.Model):
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
""" Check that the user has the right to view the ticket """Check that the user has the right to view the ticket
or that it is the author""" or that it is the author."""
if ( if (
not user_request.has_perm("tickets.view_ticket") not user_request.has_perm("tickets.view_ticket")
and self.user != user_request and self.user != user_request
@ -136,7 +155,7 @@ class Ticket(AclMixin, models.Model):
@staticmethod @staticmethod
def can_view_all(user_request, *_args, **_kwargs): 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") can = user_request.has_perm("tickets.view_ticket")
return ( return (
can, can,
@ -147,12 +166,23 @@ class Ticket(AclMixin, models.Model):
) )
def can_create(user_request, *_args, **_kwargs): def can_create(user_request, *_args, **_kwargs):
""" Authorise all users to open tickets """ """Authorise all users to open tickets."""
return True, None, None return True, None, None
class CommentTicket(AclMixin, models.Model): 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) date = models.DateTimeField(auto_now_add=True)
comment = models.TextField( comment = models.TextField(
max_length=4095, 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 return CommentTicket.objects.filter(parent_ticket=self.parent_ticket, pk__lt=self.pk).count() + 1
def can_view(self, user_request, *_args, **_kwargs): def can_view(self, user_request, *_args, **_kwargs):
""" Check that the user has the right to view the ticket comment """Check that the user has the right to view the ticket comment
or that it is the author""" or that it is the author."""
if ( if (
not user_request.has_perm("tickets.view_commentticket") not user_request.has_perm("tickets.view_commentticket")
and self.parent_ticket.user != user_request and self.parent_ticket.user != user_request
@ -195,8 +225,8 @@ class CommentTicket(AclMixin, models.Model):
return True, None, None return True, None, None
def can_edit(self, user_request, *_args, **_kwargs): def can_edit(self, user_request, *_args, **_kwargs):
""" Check that the user has the right to edit the ticket comment """Check that the user has the right to edit the ticket comment
or that it is the author""" or that it is the author."""
if ( if (
not user_request.has_perm("tickets.change_commentticket") not user_request.has_perm("tickets.change_commentticket")
and (self.parent_ticket.user != user_request or self.parent_ticket.user != self.created_by) 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 @staticmethod
def can_view_all(user_request, *_args, **_kwargs): 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") can = user_request.has_perm("tickets.view_commentticket")
return ( return (
can, can,
@ -225,7 +255,9 @@ class CommentTicket(AclMixin, models.Model):
return "Comment " + str(self.comment_id) + " on " + str(self.parent_ticket) return "Comment " + str(self.comment_id) + " on " + str(self.parent_ticket)
def publish_mail(self): 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") site_url = GeneralOption.get_cached_value("main_site_url")
to_addr = TicketOption.get_cached_value("publish_address") to_addr = TicketOption.get_cached_value("publish_address")
context = {"comment": self, "site_url": site_url} context = {"comment": self, "site_url": site_url}
@ -246,7 +278,7 @@ class CommentTicket(AclMixin, models.Model):
@receiver(post_save, sender=Ticket) @receiver(post_save, sender=Ticket)
def ticket_post_save(**kwargs): 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 kwargs["created"]:
if TicketOption.get_cached_value("publish_address"): if TicketOption.get_cached_value("publish_address"):
ticket = kwargs["instance"] ticket = kwargs["instance"]
@ -255,7 +287,7 @@ def ticket_post_save(**kwargs):
@receiver(post_save, sender=CommentTicket) @receiver(post_save, sender=CommentTicket)
def comment_post_save(**kwargs): 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 kwargs["created"]:
if TicketOption.get_cached_value("publish_address"): if TicketOption.get_cached_value("publish_address"):
comment = kwargs["instance"] comment = kwargs["instance"]

View file

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

View file

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

View file

@ -42,7 +42,7 @@ from . import models
def aff_preferences(request): 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() pref, created = models.TicketOption.objects.get_or_create()
context = { context = {
"preferences": pref, "preferences": pref,

View file

@ -20,10 +20,6 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 import messages
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
@ -52,7 +48,7 @@ from .forms import NewTicketForm, EditTicketForm, CommentTicketForm
def new_ticket(request): def new_ticket(request):
""" Ticket creation view""" """View used to display the creation form of tickets."""
ticketform = NewTicketForm(request.POST or None, request=request) ticketform = NewTicketForm(request.POST or None, request=request)
if ticketform.is_valid(): if ticketform.is_valid():
ticketform.save() ticketform.save()
@ -76,7 +72,7 @@ def new_ticket(request):
@login_required @login_required
@can_view(Ticket) @can_view(Ticket)
def aff_ticket(request, ticket, ticketid): 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) comments = CommentTicket.objects.filter(parent_ticket=ticket)
return render( return render(
request, request,
@ -88,7 +84,7 @@ def aff_ticket(request, ticket, ticketid):
@login_required @login_required
@can_edit(Ticket) @can_edit(Ticket)
def change_ticket_status(request, ticket, ticketid): 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.solved = not ticket.solved
ticket.save() ticket.save()
return redirect( return redirect(
@ -99,7 +95,7 @@ def change_ticket_status(request, ticket, ticketid):
@login_required @login_required
@can_edit(Ticket) @can_edit(Ticket)
def edit_ticket(request, ticket, ticketid): 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) ticketform = EditTicketForm(request.POST or None, instance=ticket)
if ticketform.is_valid(): if ticketform.is_valid():
ticketform.save() ticketform.save()
@ -120,7 +116,7 @@ def edit_ticket(request, ticket, ticketid):
@login_required @login_required
@can_view(Ticket) @can_view(Ticket)
def add_comment(request, ticket, ticketid): 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) commentticket = CommentTicketForm(request.POST or None, request=request)
if commentticket.is_valid(): if commentticket.is_valid():
commentticket = commentticket.save(commit=False) commentticket = commentticket.save(commit=False)
@ -139,7 +135,7 @@ def add_comment(request, ticket, ticketid):
@login_required @login_required
@can_edit(CommentTicket) @can_edit(CommentTicket)
def edit_comment(request, commentticket_instance, **_kwargs): 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) commentticket = CommentTicketForm(request.POST or None, instance=commentticket_instance)
if commentticket.is_valid(): if commentticket.is_valid():
ticketid = commentticket_instance.parent_ticket.id ticketid = commentticket_instance.parent_ticket.id
@ -157,7 +153,7 @@ def edit_comment(request, commentticket_instance, **_kwargs):
@login_required @login_required
@can_delete(CommentTicket) @can_delete(CommentTicket)
def del_comment(request, commentticket, **_kwargs): 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": if request.method == "POST":
ticketid = commentticket.parent_ticket.id ticketid = commentticket.parent_ticket.id
commentticket.delete() commentticket.delete()
@ -173,7 +169,7 @@ def del_comment(request, commentticket, **_kwargs):
@login_required @login_required
@can_view_all(Ticket) @can_view_all(Ticket)
def aff_tickets(request): def aff_tickets(request):
""" View to display all the tickets """ """View used to display all tickets."""
tickets_list = Ticket.objects.all().order_by("-date") tickets_list = Ticket.objects.all().order_by("-date")
nbr_tickets = tickets_list.count() nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).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) return render(request, "tickets/index.html", context=context)
# views cannoniques des apps optionnels # Canonic views for optional apps
def profil(request, user): 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") tickets_list = Ticket.objects.filter(user=user).all().order_by("-date")
nbr_tickets = tickets_list.count() nbr_tickets = tickets_list.count()
nbr_tickets_unsolved = tickets_list.filter(solved=False).count() nbr_tickets_unsolved = tickets_list.filter(solved=False).count()
@ -223,16 +219,19 @@ def profil(request, user):
def contact(request): def contact(request):
"""View to display a contact address on the contact page """View used to display contact addresses to be used for tickets on the
used here to display a link to open a ticket""" contact page.
"""
return render_to_string("tickets/contact.html") return render_to_string("tickets/contact.html")
def navbar_user(): 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")) return ("users", render_to_string("tickets/navbar.html"))
def navbar_logout(): 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") 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 # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """topologie.admin
Fichier définissant les administration des models dans l'interface admin The objects, fields and datastructures visible in the Django admin view.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
@ -45,67 +45,67 @@ from .models import (
class StackAdmin(VersionAdmin): class StackAdmin(VersionAdmin):
"""Administration d'une stack de switches (inclus des switches)""" """Admin class of stacks (includes switches)."""
pass pass
class SwitchAdmin(VersionAdmin): class SwitchAdmin(VersionAdmin):
"""Administration d'un switch""" """Admin class of switches."""
pass pass
class PortAdmin(VersionAdmin): class PortAdmin(VersionAdmin):
"""Administration d'un port de switches""" """Admin class of switch ports."""
pass pass
class AccessPointAdmin(VersionAdmin): class AccessPointAdmin(VersionAdmin):
"""Administration d'une borne""" """Admin class of APs."""
pass pass
class RoomAdmin(VersionAdmin): class RoomAdmin(VersionAdmin):
"""Administration d'un chambre""" """Admin class of rooms."""
pass pass
class ModelSwitchAdmin(VersionAdmin): class ModelSwitchAdmin(VersionAdmin):
"""Administration d'un modèle de switch""" """Admin class of switch models."""
pass pass
class ConstructorSwitchAdmin(VersionAdmin): class ConstructorSwitchAdmin(VersionAdmin):
"""Administration d'un constructeur d'un switch""" """Admin class of switch constructors."""
pass pass
class SwitchBayAdmin(VersionAdmin): class SwitchBayAdmin(VersionAdmin):
"""Administration d'une baie de brassage""" """Admin class of switch bays."""
pass pass
class BuildingAdmin(VersionAdmin): class BuildingAdmin(VersionAdmin):
"""Administration d'un batiment""" """Admin class of buildings."""
pass pass
class DormitoryAdmin(VersionAdmin): class DormitoryAdmin(VersionAdmin):
"""Administration d'une residence""" """Admin class of dormitories."""
pass pass
class PortProfileAdmin(VersionAdmin): class PortProfileAdmin(VersionAdmin):
"""Administration of a port profile""" """Admin class of port profiles."""
pass pass

View file

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

View file

@ -21,18 +21,15 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 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 : The following models are defined:
* stack (grouping switches): id, id_min, id_max and name
- stack (id, id_min, id_max et nom) regrouppant les switches * switch: name, number of ports, related interface and machine (MAC
- switch : nom, nombre de port, et interface address, IP address etc.) (see machines.models.interface)
machine correspondante (mac, ip, etc) (voir machines.models.interface) * port: related to a switch by foreign_key, number of the port, related
- Port: relié à un switch parent par foreign_key, numero du port, exclusively to another port, machine (server or AP) or room outlets
relié de façon exclusive à un autre port, une machine * room: list of outlets, name and comments about the plug's state
(serveur ou borne) ou une prise murale
- room : liste des prises murales, nom et commentaire de l'état de
la prise
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
@ -56,9 +53,15 @@ from re2o.mixins import AclMixin, RevMixin
class Stack(AclMixin, RevMixin, models.Model): class Stack(AclMixin, RevMixin, models.Model):
"""Un objet stack. Regrouppe des switchs en foreign key """Switch stack.
,contient une id de stack, un switch id min et max dans
le 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) name = models.CharField(max_length=32, blank=True, null=True)
stack_id = models.CharField(max_length=32, unique=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) super(Stack, self).save(*args, **kwargs)
def clean(self): 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: if self.member_id_max < self.member_id_min:
raise ValidationError( raise ValidationError(
{"member_id_max": _("The maximum ID is less than the minimum ID.")} {"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): 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( location = models.CharField(
@ -107,17 +111,16 @@ class AccessPoint(Machine):
verbose_name_plural = _("access points") verbose_name_plural = _("access points")
def port(self): 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) return Port.objects.filter(machine_interface__machine=self)
def switch(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) return Switch.objects.filter(ports__machine_interface__machine=self)
def building(self): def building(self):
""" """Return the building of the AP/Server (building of the switches
Return the building of the AP/Server (building of the switchs connected to...).
connected to...)
""" """
return Building.objects.filter(switchbay__switch=self.switch()) return Building.objects.filter(switchbay__switch=self.switch())
@ -127,7 +130,14 @@ class AccessPoint(Machine):
@classmethod @classmethod
def all_ap_in(cls, building_instance): 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( return cls.objects.filter(
interface__port__switch__switchbay__building=building_instance interface__port__switch__switchbay__building=building_instance
) )
@ -158,25 +168,24 @@ class AccessPoint(Machine):
class Server(Machine): class Server(Machine):
""" """Dummy class, to retrieve servers of a building, or get switch of a
Dummy class, to retrieve servers of a building, or get switch of a server server.
""" """
class Meta: class Meta:
proxy = True proxy = True
def port(self): 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) return Port.objects.filter(machine_interface__machine=self)
def switch(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) return Switch.objects.filter(ports__machine_interface__machine=self)
def building(self): def building(self):
""" """Return the building of the AP/Server (building of the switches
Return the building of the AP/Server connected to...).
(building of the switchs connected to...)
""" """
return Building.objects.filter(switchbay__switch=self.switch()) return Building.objects.filter(switchbay__switch=self.switch())
@ -186,7 +195,14 @@ class Server(Machine):
@classmethod @classmethod
def all_server_in(cls, building_instance): 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( return cls.objects.filter(
interface__port__switch__switchbay__building=building_instance interface__port__switch__switchbay__building=building_instance
).exclude(accesspoint__isnull=False) ).exclude(accesspoint__isnull=False)
@ -217,17 +233,19 @@ class Server(Machine):
class Switch(Machine): class Switch(Machine):
""" Definition d'un switch. Contient un nombre de ports (number), """Switch.
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
Validation au save que l'id du stack est bien dans le range id_min Attributes:
id_max de la stack parente""" 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.")) number = models.PositiveIntegerField(help_text=_("Number of ports."))
stack = models.ForeignKey( stack = models.ForeignKey(
@ -269,8 +287,9 @@ class Switch(Machine):
verbose_name_plural = _("switches") verbose_name_plural = _("switches")
def clean(self): def clean(self):
""" Verifie que l'id stack est dans le bon range """Check if the stack member ID is in the range of the stack's IDs and
Appelle également le clean de la classe parente""" calls the clean of the parent class.
"""
super(Switch, self).clean() super(Switch, self).clean()
if self.stack is not None: if self.stack is not None:
if self.stack_member_id is not None: if self.stack_member_id is not None:
@ -291,8 +310,12 @@ class Switch(Machine):
) )
def create_ports(self, begin, end): def create_ports(self, begin, end):
""" Crée les ports de begin à end si les valeurs données """Create ports for the switch if the values are consistent.
sont cohérentes. """
Args:
begin: the number of the start port.
end: the number of the end port.
"""
if end < begin: if end < begin:
raise ValidationError(_("The end port is less than the start port.")) raise ValidationError(_("The end port is less than the start port."))
ports_to_create = range(begin, end + 1) ports_to_create = range(begin, end + 1)
@ -313,8 +336,7 @@ class Switch(Machine):
) )
def main_interface(self): def main_interface(self):
""" Returns the 'main' interface of the switch """Get the main interface of the switch (the management interface)."""
It must the the management interface for that device"""
switch_iptype = OptionalTopologie.get_cached_value("switchs_ip_type") switch_iptype = OptionalTopologie.get_cached_value("switchs_ip_type")
if switch_iptype: if switch_iptype:
return ( return (
@ -329,12 +351,14 @@ class Switch(Machine):
@cached_property @cached_property
def get_radius_key(self): 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() return self.radius_key or RadiusKey.objects.filter(default_switch=True).first()
@cached_property @cached_property
def get_radius_key_value(self): 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: if self.get_radius_key:
return self.get_radius_key.radius_key return self.get_radius_key.radius_key
else: else:
@ -362,7 +386,7 @@ class Switch(Machine):
@cached_property @cached_property
def get_management_cred(self): def get_management_cred(self):
"""Retourne l'objet des creds de managament de ce switch""" """Get the management credentials objects of the switch."""
return ( return (
self.management_creds self.management_creds
or SwitchManagementCred.objects.filter(default_switch=True).first() or SwitchManagementCred.objects.filter(default_switch=True).first()
@ -370,7 +394,9 @@ class Switch(Machine):
@cached_property @cached_property
def get_management_cred_value(self): 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: if self.get_management_cred:
return { return {
"id": self.get_management_cred.management_id, "id": self.get_management_cred.management_id,
@ -401,17 +427,19 @@ class Switch(Machine):
@cached_property @cached_property
def ipv4(self): 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) return str(self.main_interface().ipv4)
@cached_property @cached_property
def ipv6(self): 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()) return str(self.main_interface().ipv6().first())
@cached_property @cached_property
def interfaces_subnet(self): 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( return dict(
( (
str(interface.ipv4), str(interface.ipv4),
@ -423,7 +451,9 @@ class Switch(Machine):
@cached_property @cached_property
def interfaces6_subnet(self): 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( return dict(
( (
str(interface.ipv6().first()), str(interface.ipv6().first()),
@ -434,7 +464,9 @@ class Switch(Machine):
@cached_property @cached_property
def list_modules(self): 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 = [] modules = []
if getattr(self.model, "is_modular", None): if getattr(self.model, "is_modular", None):
if self.model.is_itself_module: if self.model.is_itself_module:
@ -445,7 +477,7 @@ class Switch(Machine):
@cached_property @cached_property
def get_dormitory(self): def get_dormitory(self):
"""Returns the dormitory of that switch""" """Get the dormitory in which the switch is located."""
if self.switchbay: if self.switchbay:
return self.switchbay.building.dormitory return self.switchbay.building.dormitory
else: else:
@ -453,19 +485,19 @@ class Switch(Machine):
@classmethod @classmethod
def nothing_profile(cls): def nothing_profile(cls):
"""Return default nothing port profile""" """Return default nothing port profile."""
nothing_profile, _created = PortProfile.objects.get_or_create( nothing_profile, _created = PortProfile.objects.get_or_create(
profil_default="nothing", name="nothing", radius_type="NO" profil_default="nothing", name="nothing", radius_type="NO"
) )
return nothing_profile return nothing_profile
def profile_type_or_nothing(self, profile_type): 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 If it exists, return the defined default profile for a profile type on
the switch belongs the dormitory which the switch belongs.
Otherwise, return the nothing profile.
Otherwise, returns the nothing profile""" """
profile_queryset = PortProfile.objects.filter(profil_default=profile_type) profile_queryset = PortProfile.objects.filter(profil_default=profile_type)
if self.get_dormitory: if self.get_dormitory:
port_profile = ( port_profile = (
@ -478,22 +510,22 @@ class Switch(Machine):
@cached_property @cached_property
def default_uplink_profile(self): 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") return self.profile_type_or_nothing("uplink")
@cached_property @cached_property
def default_access_point_profile(self): 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") return self.profile_type_or_nothing("access_point")
@cached_property @cached_property
def default_room_profile(self): 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") return self.profile_type_or_nothing("room")
@cached_property @cached_property
def default_asso_machine_profile(self): 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") return self.profile_type_or_nothing("asso_machine")
def __str__(self): def __str__(self):
@ -522,7 +554,16 @@ class Switch(Machine):
class ModelSwitch(AclMixin, RevMixin, models.Model): 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) reference = models.CharField(max_length=255)
commercial_name = models.CharField(max_length=255, null=True, blank=True) 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): 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( reference = models.CharField(
max_length=255, max_length=255,
@ -575,7 +621,13 @@ class ModuleSwitch(AclMixin, RevMixin, models.Model):
class ModuleOnSwitch(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) module = models.ForeignKey("ModuleSwitch", on_delete=models.CASCADE)
switch = models.ForeignKey("Switch", 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): 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) name = models.CharField(max_length=255)
@ -617,7 +673,13 @@ class ConstructorSwitch(AclMixin, RevMixin, models.Model):
class SwitchBay(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) name = models.CharField(max_length=255)
building = models.ForeignKey("Building", on_delete=models.PROTECT) building = models.ForeignKey("Building", on_delete=models.PROTECT)
@ -633,8 +695,11 @@ class SwitchBay(AclMixin, RevMixin, models.Model):
class Dormitory(AclMixin, RevMixin, models.Model): class Dormitory(AclMixin, RevMixin, models.Model):
"""A student accomodation/dormitory """Dormitory.
Une résidence universitaire"""
Attributes:
name: the name of the dormitory.
"""
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
@ -644,7 +709,7 @@ class Dormitory(AclMixin, RevMixin, models.Model):
verbose_name_plural = _("dormitories") verbose_name_plural = _("dormitories")
def all_ap_in(self): 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()) return AccessPoint.all_ap_in(self.building_set.all())
@classmethod @classmethod
@ -660,8 +725,13 @@ class Dormitory(AclMixin, RevMixin, models.Model):
class Building(AclMixin, RevMixin, models.Model): class Building(AclMixin, RevMixin, models.Model):
"""A building of a dormitory """Building.
Un batiment"""
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) name = models.CharField(max_length=255)
dormitory = models.ForeignKey("Dormitory", on_delete=models.PROTECT) dormitory = models.ForeignKey("Dormitory", on_delete=models.PROTECT)
@ -672,7 +742,7 @@ class Building(AclMixin, RevMixin, models.Model):
verbose_name_plural = _("buildings") verbose_name_plural = _("buildings")
def all_ap_in(self): def all_ap_in(self):
"""Returns all ap of the building""" """Get all the APs in the building."""
return AccessPoint.all_ap_in(self) return AccessPoint.all_ap_in(self)
def get_name(self): def get_name(self):
@ -690,21 +760,32 @@ class Building(AclMixin, RevMixin, models.Model):
class Port(AclMixin, RevMixin, models.Model): class Port(AclMixin, RevMixin, models.Model):
""" Definition d'un port. Relié à un switch(foreign_key), """Port of a switch.
un port peut etre relié de manière exclusive à :
- une chambre (room) A port is related exclusively to either:
- une machine (serveur etc) (machine_interface) * a room
- un autre port (uplink) (related) * a machine, e.g. server
Champs supplémentaires : * another port
- RADIUS (mode STRICT : connexion sur port uniquement si machine Behaviour according to the RADIUS mode:
d'un adhérent à jour de cotisation et que la chambre est également à * STRICT: connection only if the machine and room have access
jour de cotisation * COMMON: check only the machine's state
mode COMMON : vérification uniquement du statut de la machine * NO: accept only request coming from the port and set on the standard
mode NO : accepte toute demande venant du port et place sur le vlan normal VLAN.
mode BLOQ : rejet de toute authentification * BLOQ: reject all requests.
- vlan_force : override la politique générale de placement vlan, permet The VLAN can be forced to override the general policy for VLAN setting.
de forcer un port sur un vlan particulier. S'additionne à la politique This enables to force a port to a particular VLAN. It adds to the RADIUS
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) switch = models.ForeignKey("Switch", related_name="ports", on_delete=models.CASCADE)
port = models.PositiveIntegerField() port = models.PositiveIntegerField()
@ -733,7 +814,7 @@ class Port(AclMixin, RevMixin, models.Model):
@cached_property @cached_property
def pretty_name(self): def pretty_name(self):
"""More elaborated name for label on switch conf""" """More elaborated name for label on switch configuration."""
if self.related: if self.related:
return _("Uplink: ") + self.related.switch.short_name return _("Uplink: ") + self.related.switch.short_name
elif self.machine_interface: elif self.machine_interface:
@ -745,14 +826,13 @@ class Port(AclMixin, RevMixin, models.Model):
@cached_property @cached_property
def get_port_profile(self): def get_port_profile(self):
"""Return the config profil for this port """Get the configuration profile 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"""
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: if self.custom_profile:
return self.custom_profile return self.custom_profile
elif self.related: elif self.related:
@ -779,26 +859,25 @@ class Port(AclMixin, RevMixin, models.Model):
) )
def make_port_related(self): def make_port_related(self):
""" Synchronise le port distant sur self""" """Synchronise the related port with self."""
related_port = self.related related_port = self.related
related_port.related = self related_port.related = self
related_port.save() related_port.save()
def clean_port_related(self): def clean_port_related(self):
""" Supprime la relation related sur self""" """Delete the related relation on self."""
related_port = self.related_port related_port = self.related_port
related_port.related = None related_port.related = None
related_port.save() related_port.save()
def clean(self): 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.... Check if the port is only related exclusively to either a room, a
Verifie que le related n'est pas déjà occupé par une machine ou une machine or another port.
chambre. Si ce n'est pas le cas, applique la relation related Check if the related port is not self and applies the relation to the
Si un port related point vers self, on nettoie la relation related port if the relation is correct.
A priori pas d'autre solution que de faire ça à la main. A priori Delete the relation if it points to self.
tout cela est dans un bloc transaction, donc pas de problème de """
cohérence"""
if hasattr(self, "switch"): if hasattr(self, "switch"):
if self.port > self.switch.number: if self.port > self.switch.number:
raise ValidationError( raise ValidationError(
@ -835,7 +914,13 @@ class Port(AclMixin, RevMixin, models.Model):
class Room(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) name = models.CharField(max_length=255)
details = models.CharField(max_length=255, blank=True) details = models.CharField(max_length=255, blank=True)
@ -853,7 +938,28 @@ class Room(AclMixin, RevMixin, models.Model):
class PortProfile(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"))) TYPES = (("NO", "NO"), ("802.1X", "802.1X"), ("MAC-radius", _("MAC-RADIUS")))
MODES = (("STRICT", "STRICT"), ("COMMON", "COMMON")) MODES = (("STRICT", "STRICT"), ("COMMON", "COMMON"))
@ -983,7 +1089,7 @@ class PortProfile(AclMixin, RevMixin, models.Model):
return ",".join(self.security_parameters_enabled) return ",".join(self.security_parameters_enabled)
def clean(self): 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() super(PortProfile, self).clean()
if ( if (
self.profil_default self.profil_default
@ -1007,21 +1113,21 @@ class PortProfile(AclMixin, RevMixin, models.Model):
@receiver(post_save, sender=AccessPoint) @receiver(post_save, sender=AccessPoint)
def ap_post_save(**_kwargs): 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("unifi-ap-names")
regen("graph_topo") regen("graph_topo")
@receiver(post_delete, sender=AccessPoint) @receiver(post_delete, sender=AccessPoint)
def ap_post_delete(**_kwargs): 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("unifi-ap-names")
regen("graph_topo") regen("graph_topo")
@receiver(post_delete, sender=Stack) @receiver(post_delete, sender=Stack)
def stack_post_delete(**_kwargs): 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) 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 # 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., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """topologie.urls
Definition des urls de l'application topologie. The defined URLs for topologie app. Included in re2o.urls.
Inclu dans urls de re2o.
Fait référence aux fonctions du views
""" """
from __future__ import unicode_literals from __future__ import unicode_literals

View file

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

View file

@ -21,8 +21,10 @@
# with this program; if not, write to the Free Software Foundation, Inc., # with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
""" """
Definition des vues pour les admin. Classique, sauf pour users, Admin views basic definition, include basic definition of admin view.
on fait appel à UserChange et ServiceUserChange, forms custom
Except for Admin edition and creation of users and services users;
with AdherentAdmin, ClubAdmin and ServiceUserAdmin.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
@ -56,15 +58,26 @@ from .forms import (
class LdapUserAdmin(admin.ModelAdmin): 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") list_display = ("name", "uidNumber", "login_shell")
exclude = ("user_password", "sambat_nt_password") exclude = ("user_password", "sambat_nt_password")
search_fields = ("name",) search_fields = ("name",)
class LdapServiceUserAdmin(admin.ModelAdmin): 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",) list_display = ("name",)
exclude = ("user_password",) exclude = ("user_password",)
@ -72,63 +85,123 @@ class LdapServiceUserAdmin(admin.ModelAdmin):
class LdapUserGroupAdmin(admin.ModelAdmin): class LdapUserGroupAdmin(admin.ModelAdmin):
"""Administration du ldapusergroupe""" """LdapUserGroup Admin view.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("name", "members", "gid") list_display = ("name", "members", "gid")
search_fields = ("name",) search_fields = ("name",)
class LdapServiceUserGroupAdmin(admin.ModelAdmin): class LdapServiceUserGroupAdmin(admin.ModelAdmin):
"""Administration du ldap serviceusergroup""" """LdapServiceUserGroup Admin view.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
list_display = ("name",) list_display = ("name",)
search_fields = ("name",) search_fields = ("name",)
class SchoolAdmin(VersionAdmin): class SchoolAdmin(VersionAdmin):
"""Administration, gestion des écoles""" """School Admin view and management.
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass pass
class ListRightAdmin(VersionAdmin): class ListRightAdmin(VersionAdmin):
"""Gestion de la liste des droits existants """ListRight and groups Admin view and management.
Ne permet pas l'edition du gid (primarykey pour ldap)""" 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",) list_display = ("unix_name",)
class ListShellAdmin(VersionAdmin): 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 pass
class RequestAdmin(admin.ModelAdmin): 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") list_display = ("user", "type", "created_at", "expires_at")
class BanAdmin(VersionAdmin): class BanAdmin(VersionAdmin):
"""Gestion des bannissements""" """Ban Admin view and management, for
User Ban
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass pass
class EMailAddressAdmin(VersionAdmin): 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 pass
class WhitelistAdmin(VersionAdmin): class WhitelistAdmin(VersionAdmin):
"""Gestion des whitelist""" """Whitelist Admin view and management, for
free access whitelisted users
Parameters:
Django ModelAdmin: Apply on django ModelAdmin
"""
pass pass
class AdherentAdmin(VersionAdmin, BaseUserAdmin): 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 # The forms to add and change user instances
add_form = UserAdminForm add_form = UserAdminForm
@ -179,6 +252,15 @@ class AdherentAdmin(VersionAdmin, BaseUserAdmin):
class ClubAdmin(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 # The forms to add and change user instances
add_form = UserAdminForm add_form = UserAdminForm
form = UserAdminForm form = UserAdminForm
@ -225,8 +307,15 @@ class ClubAdmin(VersionAdmin, BaseUserAdmin):
class ServiceUserAdmin(VersionAdmin, BaseUserAdmin): class ServiceUserAdmin(VersionAdmin, BaseUserAdmin):
"""Gestion d'un service user admin : champs personnels, """ServiceUser Admin view and management, for
mot de passe; etc""" 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 # The forms to add and change user instances
form = ServiceUserAdminForm form = ServiceUserAdminForm

View file

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

View file

@ -27,13 +27,30 @@
# Lara Kermarec, Gabriel Détraz, Lemesle Augustin # Lara Kermarec, Gabriel Détraz, Lemesle Augustin
# Gplv2 # Gplv2
""" """
Module des views. Django users views module.
On définit les vues pour l'ajout, l'edition des users : infos personnelles, Here are defined all functions of views, for the users re2o application. This views
mot de passe, etc 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 from __future__ import unicode_literals
@ -117,8 +134,17 @@ import os
@can_create(Adherent) @can_create(Adherent)
def new_user(request): def new_user(request):
""" Vue de création d'un nouvel utilisateur, """View for new Adherent/User form creation.
envoie un mail pour le mot de passe""" 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 = AdherentCreationForm(request.POST or None, request.FILES or None, user=request.user)
user.request = request user.request = request
@ -167,8 +193,17 @@ def new_user(request):
@login_required @login_required
@can_create(Club) @can_create(Club)
def new_club(request): def new_club(request):
""" Vue de création d'un nouveau club, """View for new Club/User form creation.
envoie un mail pour le mot de passe""" 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 = ClubForm(request.POST or None, request.FILES or None, user=request.user)
club.request = request club.request = request
@ -192,8 +227,16 @@ def new_club(request):
@login_required @login_required
@can_edit(Club) @can_edit(Club)
def edit_club_admin_members(request, club_instance, **_kwargs): def edit_club_admin_members(request, club_instance, **_kwargs):
"""Vue d'edition de la liste des users administrateurs et """View for editing clubs and administrators.
membres d'un club"""
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) club = ClubAdminandMembersForm(request.POST or None, request.FILES or None, instance=club_instance)
if club.is_valid(): if club.is_valid():
if club.changed_data: if club.changed_data:
@ -212,9 +255,17 @@ def edit_club_admin_members(request, club_instance, **_kwargs):
@login_required @login_required
@can_edit(User) @can_edit(User)
def edit_info(request, user, userid): def edit_info(request, user, userid):
""" Edite un utilisateur à partir de son id, """View for editing base user informations.
si l'id est différent de request.user, vérifie la Perform an acl check on user instance.
possession du droit cableur """
Parameters:
request (django request): Standard django request.
user: User instance to edit
Returns:
Django User form.
"""
if user.is_class_adherent: if user.is_class_adherent:
user_form = AdherentEditForm( user_form = AdherentEditForm(
request.POST or None, request.FILES or None, instance=user.adherent, user=request.user 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 @login_required
@can_edit(User, "state") @can_edit(User, "state")
def state(request, user, userid): 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) state_form = StateForm(request.POST or None, instance=user)
if state_form.is_valid(): if state_form.is_valid():
if state_form.changed_data: if state_form.changed_data:
@ -265,7 +327,18 @@ def state(request, user, userid):
@login_required @login_required
@can_edit(User, "groups") @can_edit(User, "groups")
def groups(request, user, userid): 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) group_form = GroupForm(request.POST or None, instance=user, user=request.user)
if group_form.is_valid(): if group_form.is_valid():
if group_form.changed_data: if group_form.changed_data:
@ -280,9 +353,20 @@ def groups(request, user, userid):
@login_required @login_required
@can_edit(User, "password") @can_edit(User, "password")
def password(request, user, userid): def password(request, user, userid):
""" Reinitialisation d'un mot de passe à partir de l'userid, """View for editing password of user.
pour self par défaut, pour tous sans droit si droit cableur, Perform an acl check on user instance, and check if editing user
pour tous si droit bureau """ 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) u_form = PassForm(request.POST or None, instance=user, user=request.user)
if u_form.is_valid(): if u_form.is_valid():
if u_form.changed_data: if u_form.changed_data:
@ -299,7 +383,20 @@ def password(request, user, userid):
@login_required @login_required
@can_edit(User, "groups") @can_edit(User, "groups")
def del_group(request, user, listrightid, **_kwargs): 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.groups.remove(ListRight.objects.get(id=listrightid))
user.save() user.save()
messages.success(request, _("%s was removed from the group.") % user) messages.success(request, _("%s was removed from the group.") % user)
@ -309,7 +406,18 @@ def del_group(request, user, listrightid, **_kwargs):
@login_required @login_required
@can_edit(User, "is_superuser") @can_edit(User, "is_superuser")
def del_superuser(request, user, **_kwargs): 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.is_superuser = False
user.save() user.save()
messages.success(request, _("%s is no longer superuser.") % user) messages.success(request, _("%s is no longer superuser.") % user)
@ -319,7 +427,18 @@ def del_superuser(request, user, **_kwargs):
@login_required @login_required
@can_create(ServiceUser) @can_create(ServiceUser)
def new_serviceuser(request): 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) user = ServiceUserForm(request.POST or None)
if user.is_valid(): if user.is_valid():
user.save() user.save()
@ -333,7 +452,19 @@ def new_serviceuser(request):
@login_required @login_required
@can_edit(ServiceUser) @can_edit(ServiceUser)
def edit_serviceuser(request, serviceuser, **_kwargs): 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) serviceuser = EditServiceUserForm(request.POST or None, instance=serviceuser)
if serviceuser.is_valid(): if serviceuser.is_valid():
if serviceuser.changed_data: if serviceuser.changed_data:
@ -348,7 +479,19 @@ def edit_serviceuser(request, serviceuser, **_kwargs):
@login_required @login_required
@can_delete(ServiceUser) @can_delete(ServiceUser)
def del_serviceuser(request, serviceuser, **_kwargs): 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": if request.method == "POST":
serviceuser.delete() serviceuser.delete()
messages.success(request, _("The service user was deleted.")) messages.success(request, _("The service user was deleted."))
@ -364,9 +507,19 @@ def del_serviceuser(request, serviceuser, **_kwargs):
@can_create(Ban) @can_create(Ban)
@can_edit(User) @can_edit(User)
def add_ban(request, user, userid): def add_ban(request, user, userid):
""" Ajouter un banissement, nécessite au moins le droit bofh """View for adding a ban object for user instance.
(a fortiori bureau) Perform an acl check on editing user, and check if editing user
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" 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_instance = Ban(user=user)
ban = BanForm(request.POST or None, instance=ban_instance) ban = BanForm(request.POST or None, instance=ban_instance)
ban.request = request ban.request = request
@ -383,9 +536,19 @@ def add_ban(request, user, userid):
@login_required @login_required
@can_edit(Ban) @can_edit(Ban)
def edit_ban(request, ban_instance, **_kwargs): def edit_ban(request, ban_instance, **_kwargs):
""" Editer un bannissement, nécessite au moins le droit bofh """View for editing a ban object for user instance.
(a fortiori bureau) Perform an acl check on editing user, and check if editing user
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement""" 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 = BanForm(request.POST or None, instance=ban_instance)
ban.request = request ban.request = request
@ -400,7 +563,18 @@ def edit_ban(request, ban_instance, **_kwargs):
@login_required @login_required
@can_delete(Ban) @can_delete(Ban)
def del_ban(request, ban, **_kwargs): 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": if request.method == "POST":
ban.delete() ban.delete()
messages.success(request, _("The ban was deleted.")) messages.success(request, _("The ban was deleted."))
@ -412,10 +586,19 @@ def del_ban(request, ban, **_kwargs):
@can_create(Whitelist) @can_create(Whitelist)
@can_edit(User) @can_edit(User)
def add_whitelist(request, user, userid): def add_whitelist(request, user, userid):
""" Accorder un accès gracieux, temporaire ou permanent. """View for adding a whitelist object for user instance.
Need droit cableur Perform an acl check on editing user, and check if editing user
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, has permission of adding a wheitelist on target user, add_whitelist.
raison obligatoire""" 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_instance = Whitelist(user=user)
whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance) whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance)
if whitelist.is_valid(): if whitelist.is_valid():
@ -434,10 +617,19 @@ def add_whitelist(request, user, userid):
@login_required @login_required
@can_edit(Whitelist) @can_edit(Whitelist)
def edit_whitelist(request, whitelist_instance, **_kwargs): def edit_whitelist(request, whitelist_instance, **_kwargs):
""" Editer un accès gracieux, temporaire ou permanent. """View for editing a whitelist object for user instance.
Need droit cableur Perform an acl check on editing user, and check if editing user
Syntaxe : JJ/MM/AAAA , heure optionnelle, prend effet immédiatement, has permission of editing a whitelist on target user, edit_whitelist.
raison obligatoire""" 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) whitelist = WhitelistForm(request.POST or None, instance=whitelist_instance)
if whitelist.is_valid(): if whitelist.is_valid():
if whitelist.changed_data: if whitelist.changed_data:
@ -452,7 +644,18 @@ def edit_whitelist(request, whitelist_instance, **_kwargs):
@login_required @login_required
@can_delete(Whitelist) @can_delete(Whitelist)
def del_whitelist(request, whitelist, **_kwargs): 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": if request.method == "POST":
whitelist.delete() whitelist.delete()
messages.success(request, _("The whitelist was deleted.")) messages.success(request, _("The whitelist was deleted."))
@ -468,7 +671,18 @@ def del_whitelist(request, whitelist, **_kwargs):
@can_create(EMailAddress) @can_create(EMailAddress)
@can_edit(User) @can_edit(User)
def add_emailaddress(request, user, userid): 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_instance = EMailAddress(user=user)
emailaddress = EMailAddressForm( emailaddress = EMailAddressForm(
request.POST or None, instance=emailaddress_instance request.POST or None, instance=emailaddress_instance
@ -487,7 +701,18 @@ def add_emailaddress(request, user, userid):
@login_required @login_required
@can_edit(EMailAddress) @can_edit(EMailAddress)
def edit_emailaddress(request, emailaddress_instance, **_kwargs): 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( emailaddress = EMailAddressForm(
request.POST or None, instance=emailaddress_instance request.POST or None, instance=emailaddress_instance
) )
@ -510,7 +735,18 @@ def edit_emailaddress(request, emailaddress_instance, **_kwargs):
@login_required @login_required
@can_delete(EMailAddress) @can_delete(EMailAddress)
def del_emailaddress(request, emailaddress, **_kwargs): 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": if request.method == "POST":
emailaddress.delete() emailaddress.delete()
messages.success(request, _("The local email account was deleted.")) messages.success(request, _("The local email account was deleted."))
@ -527,7 +763,18 @@ def del_emailaddress(request, emailaddress, **_kwargs):
@login_required @login_required
@can_edit(User) @can_edit(User)
def edit_email_settings(request, user_instance, **_kwargs): 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( email_settings = EmailSettingsForm(
request.POST or None, instance=user_instance, user=request.user request.POST or None, instance=user_instance, user=request.user
) )
@ -559,8 +806,17 @@ def edit_email_settings(request, user_instance, **_kwargs):
@login_required @login_required
@can_create(School) @can_create(School)
def add_school(request): def add_school(request):
""" Ajouter un établissement d'enseignement à la base de donnée, """View for adding a new school object.
need cableur""" 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) school = SchoolForm(request.POST or None)
if school.is_valid(): if school.is_valid():
school.save() school.save()
@ -574,8 +830,18 @@ def add_school(request):
@login_required @login_required
@can_edit(School) @can_edit(School)
def edit_school(request, school_instance, **_kwargs): def edit_school(request, school_instance, **_kwargs):
""" Editer un établissement d'enseignement à partir du schoolid dans """View for editing a school instance object.
la base de donnée, need cableur""" 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) school = SchoolForm(request.POST or None, instance=school_instance)
if school.is_valid(): if school.is_valid():
if school.changed_data: if school.changed_data:
@ -590,10 +856,20 @@ def edit_school(request, school_instance, **_kwargs):
@login_required @login_required
@can_delete_set(School) @can_delete_set(School)
def del_school(request, instances): def del_school(request, instances):
""" Supprimer un établissement d'enseignement à la base de donnée, """View for deleting a school instance object.
need cableur Perform an acl check on editing user, and check if editing user
Objet protégé, possible seulement si aucun user n'est affecté à has permission of deleting a school, del_school.
l'établissement """ 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) school = DelSchoolForm(request.POST or None, instances=instances)
if school.is_valid(): if school.is_valid():
school_dels = school.cleaned_data["schools"] school_dels = school.cleaned_data["schools"]
@ -619,7 +895,17 @@ def del_school(request, instances):
@login_required @login_required
@can_create(ListShell) @can_create(ListShell)
def add_shell(request): 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) shell = ShellForm(request.POST or None)
if shell.is_valid(): if shell.is_valid():
shell.save() shell.save()
@ -633,7 +919,18 @@ def add_shell(request):
@login_required @login_required
@can_edit(ListShell) @can_edit(ListShell)
def edit_shell(request, shell_instance, **_kwargs): 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) shell = ShellForm(request.POST or None, instance=shell_instance)
if shell.is_valid(): if shell.is_valid():
if shell.changed_data: if shell.changed_data:
@ -648,7 +945,20 @@ def edit_shell(request, shell_instance, **_kwargs):
@login_required @login_required
@can_delete(ListShell) @can_delete(ListShell)
def del_shell(request, shell, **_kwargs): 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": if request.method == "POST":
shell.delete() shell.delete()
messages.success(request, _("The shell was deleted.")) messages.success(request, _("The shell was deleted."))
@ -661,8 +971,18 @@ def del_shell(request, shell, **_kwargs):
@login_required @login_required
@can_create(ListRight) @can_create(ListRight)
def add_listright(request): def add_listright(request):
""" Ajouter un droit/groupe, nécessite droit bureau. """View for adding a new group of rights and users (listright linked to groups)
Obligation de fournir un gid pour la synchro ldap, unique """ 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) listright = NewListRightForm(request.POST or None)
if listright.is_valid(): if listright.is_valid():
listright.save() listright.save()
@ -678,8 +998,18 @@ def add_listright(request):
@login_required @login_required
@can_edit(ListRight) @can_edit(ListRight)
def edit_listright(request, listright_instance, **_kwargs): def edit_listright(request, listright_instance, **_kwargs):
""" Editer un groupe/droit, necessite droit bureau, """View for editing a listright instance object.
à partir du listright id """ 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) listright_form = ListRightForm(request.POST or None, instance=listright_instance)
if listright_form.is_valid(): if listright_form.is_valid():
if listright_form.changed_data: if listright_form.changed_data:
@ -701,8 +1031,20 @@ def edit_listright(request, listright_instance, **_kwargs):
@login_required @login_required
@can_delete_set(ListRight) @can_delete_set(ListRight)
def del_listright(request, instances): def del_listright(request, instances):
""" Supprimer un ou plusieurs groupe, possible si il est vide, need droit """View for deleting a listright instance object.
bureau """ 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) listright = DelListRightForm(request.POST or None, instances=instances)
if listright.is_valid(): if listright.is_valid():
listright_dels = listright.cleaned_data["listrights"] listright_dels = listright.cleaned_data["listrights"]
@ -729,7 +1071,17 @@ def del_listright(request, instances):
@can_view_all(User) @can_view_all(User)
@can_change(User, "state") @can_change(User, "state")
def mass_archive(request): 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") pagination_number = GeneralOption.get_cached_value("pagination_number")
to_archive_form = MassArchiveForm(request.POST or None) to_archive_form = MassArchiveForm(request.POST or None)
to_archive_list = [] to_archive_list = []
@ -764,7 +1116,16 @@ def mass_archive(request):
@login_required @login_required
@can_view_all(Adherent) @can_view_all(Adherent)
def index(request): 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") pagination_number = GeneralOption.get_cached_value("pagination_number")
users_list = Adherent.objects.select_related("room") users_list = Adherent.objects.select_related("room")
users_list = SortTable.sort( users_list = SortTable.sort(
@ -780,7 +1141,16 @@ def index(request):
@login_required @login_required
@can_view_all(Club) @can_view_all(Club)
def index_clubs(request): 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") pagination_number = GeneralOption.get_cached_value("pagination_number")
clubs_list = Club.objects.select_related("room") clubs_list = Club.objects.select_related("room")
clubs_list = SortTable.sort( clubs_list = SortTable.sort(
@ -796,7 +1166,16 @@ def index_clubs(request):
@login_required @login_required
@can_view_all(Ban) @can_view_all(Ban)
def index_ban(request): 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") pagination_number = GeneralOption.get_cached_value("pagination_number")
ban_list = Ban.objects.select_related("user") ban_list = Ban.objects.select_related("user")
ban_list = SortTable.sort( ban_list = SortTable.sort(
@ -812,7 +1191,16 @@ def index_ban(request):
@login_required @login_required
@can_view_all(Whitelist) @can_view_all(Whitelist)
def index_white(request): 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") pagination_number = GeneralOption.get_cached_value("pagination_number")
white_list = Whitelist.objects.select_related("user") white_list = Whitelist.objects.select_related("user")
white_list = SortTable.sort( white_list = SortTable.sort(
@ -828,7 +1216,16 @@ def index_white(request):
@login_required @login_required
@can_view_all(School) @can_view_all(School)
def index_school(request): 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") school_list = School.objects.order_by("name")
pagination_number = GeneralOption.get_cached_value("pagination_number") pagination_number = GeneralOption.get_cached_value("pagination_number")
school_list = SortTable.sort( school_list = SortTable.sort(
@ -844,7 +1241,16 @@ def index_school(request):
@login_required @login_required
@can_view_all(ListShell) @can_view_all(ListShell)
def index_shell(request): 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") shell_list = ListShell.objects.order_by("shell")
return render(request, "users/index_shell.html", {"shell_list": shell_list}) return render(request, "users/index_shell.html", {"shell_list": shell_list})
@ -852,7 +1258,17 @@ def index_shell(request):
@login_required @login_required
@can_view_all(ListRight) @can_view_all(ListRight)
def index_listright(request): 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 = {} rights = {}
for right in ( for right in (
ListRight.objects.order_by("name") ListRight.objects.order_by("name")
@ -875,7 +1291,17 @@ def index_listright(request):
@login_required @login_required
@can_view_all(ServiceUser) @can_view_all(ServiceUser)
def index_serviceusers(request): 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") serviceusers_list = ServiceUser.objects.order_by("pseudo")
return render( return render(
request, request,
@ -886,14 +1312,42 @@ def index_serviceusers(request):
@login_required @login_required
def mon_profil(request): 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)})) return redirect(reverse("users:profil", kwargs={"userid": str(request.user.id)}))
@login_required @login_required
@can_view(User) @can_view(User)
def profil(request, users, **_kwargs): 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 = ( machines = (
Machine.objects.filter(user=users) Machine.objects.filter(user=users)
.select_related("user") .select_related("user")
@ -969,7 +1423,17 @@ def profil(request, users, **_kwargs):
def reset_password(request): 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) userform = ResetPasswordForm(request.POST or None)
if userform.is_valid(): if userform.is_valid():
try: try:
@ -994,8 +1458,17 @@ def reset_password(request):
def process(request, token): def process(request, token):
"""Process, lien pour la reinitialisation du mot de passe """Process view, in case of both reset password, or confirm email in case
et la confirmation de l'email""" 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()) valid_reqs = Request.objects.filter(expires_at__gt=timezone.now())
req = get_object_or_404(valid_reqs, token=token) req = get_object_or_404(valid_reqs, token=token)
@ -1009,8 +1482,16 @@ def process(request, token):
def process_passwd(request, req): def process_passwd(request, req):
"""Process le changeemnt de mot de passe, renvoie le formulaire """Process view, in case of reset password by email. Returns
demandant le nouveau password""" a form to change and reset the password.
Parameters:
request (django request): Standard django request.
Returns:
Correct Django process password Form.
"""
user = req.user user = req.user
u_form = PassForm(request.POST or None, instance=user, user=request.user) u_form = PassForm(request.POST or None, instance=user, user=request.user)
if u_form.is_valid(): if u_form.is_valid():
@ -1031,8 +1512,17 @@ def process_passwd(request, req):
def process_email(request, req): def process_email(request, req):
"""Process la confirmation de mail, renvoie le formulaire """Process view, in case of confirm a new email. Returns
de validation""" 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 user = req.user
if request.method == "POST": if request.method == "POST":
with transaction.atomic(), reversion.create_revision(): with transaction.atomic(), reversion.create_revision():
@ -1055,7 +1545,16 @@ def process_email(request, req):
@login_required @login_required
@can_edit(User) @can_edit(User)
def resend_confirmation_email(request, logged_user, userid): 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: try:
user = User.objects.get( user = User.objects.get(
id=userid, id=userid,
@ -1074,6 +1573,20 @@ def resend_confirmation_email(request, logged_user, userid):
@login_required @login_required
def initial_register(request): 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_ip = request.GET.get("switch_ip", None)
switch_port = request.GET.get("switch_port", None) switch_port = request.GET.get("switch_port", None)
client_mac = request.GET.get("client_mac", None) client_mac = request.GET.get("client_mac", None)