[gest_crans_lc] Découpage dans plusieurs fichiers
Héritage multiple banzai \o/
This commit is contained in:
parent
5d477cc1ac
commit
2d04dedf7b
10 changed files with 3209 additions and 3014 deletions
465
gestion/dialog/lc.py
Normal file
465
gestion/dialog/lc.py
Normal file
|
@ -0,0 +1,465 @@
|
|||
#!/bin/bash /usr/scripts/python.sh
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
u"""
|
||||
Copyright (C) Valentin Samir
|
||||
Licence : GPLv3
|
||||
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
import ldap
|
||||
if '/usr/scripts' not in sys.path:
|
||||
sys.path.append('/usr/scripts')
|
||||
from pythondialog import Dialog
|
||||
from pythondialog import DialogError, DialogTerminatedBySignal
|
||||
|
||||
from gestion.affich_tools import get_screen_size, coul
|
||||
|
||||
import lc_ldap.shortcuts
|
||||
import lc_ldap.objets as objets
|
||||
import lc_ldap.attributs as attributs
|
||||
import lc_ldap.printing as printing
|
||||
|
||||
import CPS
|
||||
from CPS import TailCall, tailcaller, Continue, TailCaller
|
||||
|
||||
|
||||
class Dialog(CPS.Dialog):
|
||||
def __init__(self, debug_enable=False, ldap_test=False):
|
||||
super(Dialog, self).__init__(debug_enable=debug_enable)
|
||||
# On initialise le moteur de rendu en spécifiant qu'on va faire du dialog
|
||||
printing.template(dialog=True)
|
||||
self.ldap_test = ldap_test
|
||||
self.check_ldap()
|
||||
|
||||
def has_right(self, liste, obj=None):
|
||||
"""Vérifie que l'un des droits de l'utilisateur courant est inclus dans list"""
|
||||
if not isinstance(liste, list):
|
||||
liste = [liste]
|
||||
if obj:
|
||||
droits = obj.rights()
|
||||
else:
|
||||
droits = self.conn.droits
|
||||
for d in liste:
|
||||
if d in droits:
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_ldap(self):
|
||||
"""Se connecte à la base ldap et vérifie les droits de l'utilisateur courant"""
|
||||
self.check_ldap_last = time.time()
|
||||
|
||||
# S'il y a --test dans les argument, on utilise la base de test
|
||||
if self.ldap_test:
|
||||
self.conn = lc_ldap.shortcuts.lc_ldap_test()
|
||||
else:
|
||||
# On ouvre une connexion lc_ldap
|
||||
self.conn = lc_ldap.shortcuts.lc_ldap_admin()
|
||||
|
||||
# On vérifie que l'utilisateur système existe dans ldap (pour la gestion des droits)
|
||||
luser=self.conn.search(u'(&(uid=%s)(objectClass=cransAccount))' % self.conn.current_login)
|
||||
if not luser:
|
||||
sys.stderr.write("L'utilisateur %s n'existe pas dans la base de donnée" % self.conn.current_login)
|
||||
sys.exit(1)
|
||||
self.conn.droits = [str(d) for d in luser[0]['droits']]
|
||||
self.conn.dn = luser[0].dn
|
||||
|
||||
# Si un nom d'utilisateur est donné sur la ligne de commande
|
||||
# et qu'on a les droits nounou, on l'utilise
|
||||
if sys.argv[1:] and attributs.nounou in self.conn.droits:
|
||||
for u in sys.argv[1:]:
|
||||
luser=self.conn.search(u'(&(uid=%s)(objectClass=cransAccount))' % u)
|
||||
if luser:
|
||||
self.conn.current_login = u
|
||||
self.conn.droits = [str(d) for d in luser[0]['droits']]
|
||||
self.conn.dn = luser[0].dn
|
||||
break
|
||||
a = attributs
|
||||
allowed_right = [a.cableur, a.tresorier, a.bureau, a.nounou, a.imprimeur]
|
||||
for droit in allowed_right:
|
||||
if droit in self.conn.droits:
|
||||
break
|
||||
else:
|
||||
sys.stderr.write(
|
||||
u"%s ne possède aucun des droits :\n * %s\nnécessaire à utiliser ce programme\n" % (
|
||||
self.conn.current_login,
|
||||
'\n * '.join(allowed_right)
|
||||
)
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
@property
|
||||
def dialog(self):
|
||||
"""
|
||||
Renvois l'objet dialog. De plus renouvelle régulièrement la connexion à la base ldap
|
||||
"""
|
||||
# Tous les self.timeout, on refraichie la connexion ldap
|
||||
if time.time() - self.check_ldap_last > self.timeout:
|
||||
self.check_ldap()
|
||||
return super(Dialog, self).dialog
|
||||
|
||||
|
||||
|
||||
def edit_boolean_attributs(self, obj, attribs, title, update_obj, cont, values={}):
|
||||
"""
|
||||
Permet d'éditer des attributs booléen de l'objet obj listé dans attribs.
|
||||
update_obj est le nom du paramètre à mettre à jour dans cont pour passer l'objet modifié
|
||||
"""
|
||||
# Dictionnaire décrivant quelle est la valeur booléenne à donner à l'absence de l'attribut
|
||||
missing = {
|
||||
'default' : False, # par défaut, on dit que c'est False
|
||||
attributs.dnsIpv6 : True # pour dnsIpv6, c'est True
|
||||
}
|
||||
choices = [(a.ldap_name, a.legend, 1 if values.get(a.ldap_name, obj[a.ldap_name][0] if obj[a.ldap_name] else missing.get(a, missing['default'])) else 0) for a in attribs]
|
||||
def box():
|
||||
return self.dialog.checklist("Activier ou désactiver les propriétés suivantes",
|
||||
height=0,
|
||||
width=0,
|
||||
timeout=self.timeout,
|
||||
list_height=7,
|
||||
choices=choices,
|
||||
title=title)
|
||||
|
||||
def todo(values, obj, attribs, cont):
|
||||
# On met à jour chaque attribut si sa valeur à changé
|
||||
with self.conn.search(dn=obj.dn, scope=0, mode='rw')[0] as obj:
|
||||
for a in attribs:
|
||||
if obj[a.ldap_name] and obj[a.ldap_name][0] != values[a.ldap_name]:
|
||||
obj[a.ldap_name]=values[a.ldap_name]
|
||||
elif not obj[a.ldap_name] and missing.get(a, missing['default']) != values[a.ldap_name]:
|
||||
obj[a.ldap_name]=values[a.ldap_name]
|
||||
obj.history_gen()
|
||||
obj.save()
|
||||
# On s'en va en mettant à jour dans la continuation la valeur de obj
|
||||
raise Continue(cont(**{update_obj:obj}))
|
||||
|
||||
(code, output) = self.handle_dialog(cont, box)
|
||||
|
||||
# On transforme la liste des cases dialog cochée en dictionnnaire
|
||||
values = dict((a.ldap_name, a.ldap_name in output) for a in attribs)
|
||||
|
||||
# Une continuation que l'on suivra si quelque chose se passe mal
|
||||
retry_cont = TailCall(self.edit_boolean_attributs, obj=obj, update_obj=update_obj, attribs=attribs, title=title, cont=cont, values=values)
|
||||
|
||||
return self.handle_dialog_result(
|
||||
code=code,
|
||||
output=output,
|
||||
cancel_cont=cont,
|
||||
error_cont=retry_cont,
|
||||
codes_todo=[([self.dialog.DIALOG_OK], todo, [values, obj, attribs, cont])]
|
||||
)
|
||||
|
||||
|
||||
def edit_attributs(self, obj, attr, title, update_obj, cont, tag=None, values=None):
|
||||
"""
|
||||
Permet d'éditer la liste d'attribut attr de l'objet obj.
|
||||
update_obj est le nom du paramètre à mettre à jour dans cont pour passer l'objet modifié
|
||||
"""
|
||||
|
||||
# Il n'y a pas inputmenu dans la lib dialog, du coup, on traite les arguments à la main.
|
||||
# Ça reste relativement acceptable comme on utilise la fonction de la lib pour appeler dialog
|
||||
def box(values, default_tag):
|
||||
cmd = ['--inputmenu', "Édition de l'attribut %s :" % attr, "0", "0", "20"]
|
||||
index=0
|
||||
for value in values:
|
||||
cmd.extend([str(index), str(value)])
|
||||
index+=1
|
||||
cmd.extend(['new', ''])
|
||||
(code, output) = self.dialog._perform(*(cmd,), timeout=self.timeout, title=title, default_item=str(default_tag))
|
||||
if code == self.dialog.DIALOG_EXTRA:
|
||||
output = output.split(' ', 2)[1:]
|
||||
else:
|
||||
output = ''
|
||||
return (code, output)
|
||||
|
||||
def todo_extra(output, values, retry_cont):
|
||||
tag, value = output
|
||||
if tag == 'new':
|
||||
if value:
|
||||
values.append(value)
|
||||
elif value == '':
|
||||
values.pop(int(tag))
|
||||
else:
|
||||
values[int(tag)] = value
|
||||
raise Continue(retry_cont(values=values, tag=tag))
|
||||
|
||||
def todo(obj, values, cont):
|
||||
with self.conn.search(dn=obj.dn, scope=0, mode='rw')[0] as obj:
|
||||
obj[attr] = [unicode(value, 'utf-8') for value in values]
|
||||
obj.history_gen()
|
||||
obj.save()
|
||||
raise Continue(cont(**{update_obj:obj}))
|
||||
|
||||
if values is None:
|
||||
values = [str(a) for a in obj[attr]]
|
||||
retry_cont = TailCall(self.edit_attributs, obj=obj, attr=attr, title=title, update_obj=update_obj, cont=cont, values=values)
|
||||
(code, output) = self.handle_dialog(cont, box, values, tag)
|
||||
|
||||
return self.handle_dialog_result(
|
||||
code=code,
|
||||
output=output,
|
||||
cancel_cont=cont,
|
||||
error_cont=retry_cont,
|
||||
codes_todo=[
|
||||
([self.dialog.DIALOG_OK], todo, [obj, values, cont]),
|
||||
([self.dialog.DIALOG_EXTRA], todo_extra, [output, values, retry_cont]),
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def search(self, objectClassS, title, values={}, cont=None, disable_field=[]):
|
||||
"""
|
||||
Rechercher des adhérents ou des machines dans la base ldap
|
||||
retourne le tuple (code de retour dialog, valeurs entrée par l'utilisateur, liste d'objets trouvés)
|
||||
La fonction est découpé en trois partie :
|
||||
* affichage dialog et récupération du résultat
|
||||
* construction de filtres de recherche ldap et recherches ldap
|
||||
* filtre sur les résultats des recherches ldap
|
||||
"""
|
||||
select_dict = {
|
||||
#label attribut ldap search for substring param dialog: line col valeur input-line icol len max-chars
|
||||
'Nom' : {'ldap':'nom', 'sub':True, 'params' : [ 1, 1, values.get('nom', ""), 1, 13, 20, 20]},
|
||||
'Prénom' : {'ldap':'prenom', 'sub':True, 'params' : [ 2, 1, values.get('prenom', ""), 2, 13, 20, 20]},
|
||||
'Téléphone' : {'ldap':'tel', 'sub':True, 'params' : [ 3, 1, values.get('tel', ""), 3, 13, 10, 00]},
|
||||
'Chambre' : {'ldap':'chbre','sub':True, 'params' : [ 4, 1, values.get('chbre',""), 4, 13, 05, 00]},
|
||||
'aid' : {'ldap' : 'aid', 'sub':False, 'params' : [ 5, 1, values.get('aid',""), 5, 13, 05, 05]},
|
||||
'mail' : {'ldap' : 'mail', 'sub':True, 'params' : [ 6, 1, values.get('mail',""), 6, 13, 40, 00]},
|
||||
# seconde colone
|
||||
'Machine' : {'ldap' : '*', 'sub':True, 'params' : [1, 35, "", 1, 43, 0, 0]},
|
||||
'Host' : {'ldap' : 'host', 'sub':True, 'params' : [2, 37, values.get('host',""), 2, 43, 17, 17]},
|
||||
'Mac' : {'ldap' : 'macAddress', 'sub':False, 'params' : [3, 37, values.get('macAddress',""), 3, 43, 17, 17]},
|
||||
'IP' : {'ldap' : 'ipHostNumber', 'sub':False,'params' : [4, 37, values.get('ipHostNumber',""), 4, 43, 15, 15]},
|
||||
'mid' : {'ldap' : 'mid', 'sub':False, 'params' : [5, 37, values.get('mid',""), 5, 43, 5, 5]},
|
||||
}
|
||||
# On a besoin de l'ordre pour récupérer les valeurs ensuite
|
||||
select_adherent = ['Nom', 'Prénom', 'Téléphone', 'Chambre', 'aid', 'mail']
|
||||
select_machine = ['Host', 'Mac', 'IP', 'mid']
|
||||
if 'club' in objectClassS and not 'adherent' in objectClassS:
|
||||
select_dict['cid']=select_dict['aid']
|
||||
select_dict['cid']['ldap']='cid'
|
||||
select_dict['cid']['params'][2]=values.get('cid', "")
|
||||
select_adherent[select_adherent.index('aid')]='cid'
|
||||
def box():
|
||||
# On met les argument à dialog à la main ici, sinon, c'est difficile de choisir comment mettre une seconde colone
|
||||
cmd = ["--mixedform", "Entrez vos paramètres de recherche", '0', '0', '0']
|
||||
for key in select_adherent:
|
||||
cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']] + ['2' if key in disable_field else '0'])
|
||||
cmd.extend(['Machine :'] + [str(e) for e in select_dict['Machine']['params']] + ['2'])
|
||||
for key in select_machine:
|
||||
cmd.extend(['%s :' % key] + [str(e) for e in select_dict[key]['params']] + ['2' if key in disable_field else '0'])
|
||||
cmd.extend(["Les champs vides sont ignorés.", '7', '1', "", '0', '0', '0', '0', '2' ])
|
||||
# On utilise quand même la fonction de la bibliothèques pour passer les arguments
|
||||
(code, output) = self.dialog._perform(*(cmd,), timeout=self.timeout, title=title, backtitle="Entrez vos paramètres de recherche")
|
||||
if output:
|
||||
return (code, output.split('\n')[:-1])
|
||||
else: # empty selection
|
||||
return (code, [])
|
||||
|
||||
(code, dialog_values) = self.handle_dialog(cont, box)
|
||||
# Si il a appuyé sur annuler ou sur escape, on saute sur la continuation
|
||||
if code in (self.dialog.DIALOG_CANCEL, self.dialog.DIALOG_ESC):
|
||||
raise Continue(cont)
|
||||
else:
|
||||
# Transformation de la liste des valeures entrée en dictionnnaire
|
||||
dialog_values = dict(zip(select_adherent + select_machine, dialog_values))
|
||||
ldap_values = dict([(select_dict[key]['ldap'], value) for key, value in dialog_values.items()])
|
||||
|
||||
# Construction des filtres ldap pour les adhérents et les machines
|
||||
filter_adherent = []
|
||||
filter_machine = []
|
||||
for (key, value) in dialog_values.items():
|
||||
if value:
|
||||
if key in select_adherent:
|
||||
filter_adherent.append((u"(%s=*%s*)" if select_dict[key]['sub'] and not '*' in value else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8')))
|
||||
elif key in select_machine:
|
||||
filter_machine.append((u"(%s=*%s*)" if select_dict[key]['sub'] and not '*' in value else u"(%s=%s)") % (select_dict[key]['ldap'], unicode(value, 'utf-8')))
|
||||
if filter_adherent:
|
||||
filter_adherent=u"(&%s)" % "".join(filter_adherent)
|
||||
if filter_machine:
|
||||
filter_machine=u"(&%s)" % "".join(filter_machine)
|
||||
|
||||
# Récupération des adhérents et des machines
|
||||
adherents=self.conn.search(filter_adherent) if filter_adherent else []
|
||||
machines=self.conn.search(filter_machine) if filter_machine else []
|
||||
|
||||
# Filtrage des machines en fonction des adhérents
|
||||
if filter_adherent:
|
||||
if filter_machine:
|
||||
# Si on filtre sur des adhérent et des machines, on calcule l'intersection
|
||||
adherents_dn = set([a.dn for a in adherents])
|
||||
machines_f = [m for m in machines if m.parent_dn in adherents_dn]
|
||||
else:
|
||||
# Sinon on filtre seulement sur les adhérents, récupère les machines des adhérents trouvés
|
||||
machines_f = [m for a in adherents for m in a.machines()]
|
||||
else:
|
||||
# Sinon si on filtre seulement sur des machines
|
||||
machines_f = machines
|
||||
|
||||
# Filtrage des adhérents en fonction des machines
|
||||
if filter_machine:
|
||||
if filter_adherent:
|
||||
# Si on filtre sur des adhérents et des machines, on calcule l'intersection
|
||||
machines_dn = set([m.parent_dn for m in machines])
|
||||
adherents_f = [a for a in adherents if a.dn in machines_dn]
|
||||
else:
|
||||
# Sinon on récupères les proprios des machines trouvées
|
||||
adherents_f = [m.proprio() for m in machines]
|
||||
else:
|
||||
# Sinon si on filtre seulement sur des adhérents
|
||||
adherents_f = adherents
|
||||
|
||||
# On filtre sur les objectClassS
|
||||
return ldap_values, [ o for objectClass in objectClassS for o in machines_f+adherents_f if objectClass in o['objectClass'] ]
|
||||
|
||||
@tailcaller
|
||||
def select_one(self, items, title, text="Que souhaitez vous faire ?", default_item=None, cont=None):
|
||||
"""Fait selectionner un item parmis une liste d'items à l'utisateur"""
|
||||
|
||||
def box(items, default_item):
|
||||
choices=[]
|
||||
olist={}
|
||||
count = 0
|
||||
|
||||
# On sépare les item d'items en fonction de leur type
|
||||
for o in items:
|
||||
olist[o.__class__] = olist.get(o.__class__, []) + [o]
|
||||
classes = olist.keys()
|
||||
classes.sort()
|
||||
default_tag = items.index(default_item) if default_item in items else default_item
|
||||
|
||||
# On se débrouille pour faire corresponde l'ordre d'affichache des objets
|
||||
# et leur ordre dans la liste items. On donne la largeur de l'affichage à la main
|
||||
# pour prendre en compte la largeur du widget dialog
|
||||
del items[:] # On vide la liste pour la modifier en place
|
||||
items_id = {}
|
||||
(line, col) = get_screen_size()
|
||||
for c in classes:
|
||||
items.extend(olist[c])
|
||||
items_s = printing.sprint_list(olist[c], col-20).encode('utf-8').split('\n')
|
||||
choices.append(("", str(items_s[0])))
|
||||
next=1
|
||||
if items_s[next:]:
|
||||
choices.append(("", str(items_s[next])))
|
||||
next+=1
|
||||
for i in items_s[next:]:
|
||||
if i: # on zap les lignes vides
|
||||
choices.append((str(count), str(i)))
|
||||
count+=1
|
||||
# On laisse une ligne vide pour séparer les listes d'objets de type différent
|
||||
choices.append(("", ""))
|
||||
|
||||
return self.dialog.menu(
|
||||
text,
|
||||
width=0,
|
||||
height=0,
|
||||
menu_height=0,
|
||||
timeout=self.timeout,
|
||||
item_help=0,
|
||||
default_item=str(default_tag),
|
||||
title=title,
|
||||
scrollbar=True,
|
||||
choices=choices,
|
||||
colors=True)
|
||||
|
||||
|
||||
def todo(tag, items, title, cont, retry_cont):
|
||||
# Si l'utilisateur n'a pas choisis une ligne correspondant à quelque chose
|
||||
if not tag:
|
||||
self.dialog.msgbox("Merci de choisir l'un des item de la liste ou d'annuler", timeout=self.timeout, title="Sélection", width=0, height=0)
|
||||
raise Continue(retry_cont)
|
||||
# Sinon on retourne l'item choisis
|
||||
elif self.confirm_item(items[int(tag)], title):
|
||||
return items[int(tag)]
|
||||
else:
|
||||
raise Continue(cont)
|
||||
|
||||
(code, tag) = self.handle_dialog(cont, box, items, default_item)
|
||||
retry_cont = TailCall(self.select_one, items=items, title=title, default_item=tag, cont=cont)
|
||||
|
||||
return self.handle_dialog_result(
|
||||
code=code,
|
||||
output=tag,
|
||||
cancel_cont=cont,
|
||||
error_cont=retry_cont,
|
||||
codes_todo=[([self.dialog.DIALOG_OK], todo, [tag, items, title, cont, retry_cont])]
|
||||
)
|
||||
|
||||
|
||||
def confirm_item(self, item, title, defaultno=False, text='', text_bottom="", **params):
|
||||
"""Affiche un item et demande si c'est bien celui là que l'on veux (supprimer, éditer, créer,...)"""
|
||||
return self.dialog.yesno(
|
||||
text + printing.sprint(item, **params) + text_bottom,
|
||||
no_collapse=True,
|
||||
colors=True,
|
||||
no_mouse=True,
|
||||
timeout=self.timeout,
|
||||
title=title,
|
||||
defaultno=defaultno,
|
||||
width=0, height=0,
|
||||
backtitle="Appuyez sur MAJ pour selectionner du texte"
|
||||
) == self.dialog.DIALOG_OK
|
||||
|
||||
def display_item(self, item, title, **params):
|
||||
"""Affiche un item"""
|
||||
return self.dialog.msgbox(
|
||||
printing.sprint(item, **params),
|
||||
no_collapse=True,
|
||||
colors=True,
|
||||
timeout=self.timeout,
|
||||
title=title,
|
||||
width=0, height=0,
|
||||
backtitle="Appuyez sur MAJ pour selectionner du texte"
|
||||
)
|
||||
|
||||
# On a besoin du décorateur ici car select va retourner un item après avoir
|
||||
# possblement traiter plusieurs tailcall
|
||||
@tailcaller
|
||||
def select(self, objectClassS, title, values={}, cont=None, disable_field=[]):
|
||||
"""Permet de choisir un objet adhérent ou machine dans la base ldap"""
|
||||
try:
|
||||
# On fait effectuer une recherche à l'utilisateur
|
||||
values, items = self.search(objectClassS, title, values, cont=cont, disable_field=disable_field)
|
||||
# S'il n'y a pas de résultas, on recommence
|
||||
if not items:
|
||||
self.dialog.msgbox("Aucun Résultat", timeout=self.timeout, title="Recherche", width=0, height=0)
|
||||
raise Continue(TailCall(self.select, objectClassS=objectClassS, title=title, values=values, disable_field=disable_field, cont=cont))
|
||||
# S'il y a plusieurs résultats
|
||||
elif len(items)>1:
|
||||
# On en fait choisir un, si c'est une continuation qui est renvoyé, elle est gérée par select
|
||||
return self.select_one(items, title, cont=TailCall(self.select, objectClassS=objectClassS, title=title, values=values, disable_field=disable_field, cont=cont))
|
||||
# S'il y a exactement 1 résultat à la recherche, on fait confirmer son choix à l'utilisateur
|
||||
elif len(items) == 1:
|
||||
item=items[0]
|
||||
# On fait confirmer son choix à l'utilisateur
|
||||
if self.confirm_item(item, title):
|
||||
return item
|
||||
else:
|
||||
raise Continue(TailCall(self.select, objectClassS=objectClassS, title=title, values=values, disable_field=disable_field, cont=cont))
|
||||
except self.error_to_raise:
|
||||
raise
|
||||
except Exception as e:
|
||||
self.dialog.msgbox(traceback.format_exc() if self.debug_enable else "%r" % e, timeout=self.timeout, title="Erreur rencontrée", width=0, height=0)
|
||||
raise Continue(TailCall(self.select, objectClassS=objectClassS, title=title, values=values, disable_field=disable_field, cont=cont))
|
||||
|
||||
|
||||
@TailCaller
|
||||
def main(gc, cont=None):
|
||||
"""
|
||||
Fonction principale gérant l'appel au menu principal,
|
||||
le timeout des écrans dialog et les reconnexion a ldap en cas de perte de la connexion
|
||||
"""
|
||||
while True:
|
||||
try:
|
||||
# tant que le timeout est atteint on revient au menu principal
|
||||
gc.menu_principal()
|
||||
except DialogError as e:
|
||||
# Si l'erreur n'est pas due à un timeout, on la propage
|
||||
if time.time() - gc.dialog_last_access < gc.timeout:
|
||||
raise
|
||||
# Si on perd la connexion à ldap, on en ouvre une nouvelle
|
||||
except ldap.SERVER_DOWN:
|
||||
if gc.dialog.pause(title="Erreur", duration=10, height=9, width=50, text="La connection au serveur ldap à été perdue.\nTentative de reconnexion en cours…") == gc.dialog.DIALOG_OK:
|
||||
gc.check_ldap()
|
||||
else:
|
||||
return
|
Loading…
Add table
Add a link
Reference in a new issue