# -*- coding: UTF-8 -*-
###########################################################################
# Eole NG - 2007
# Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
# Licence CeCill  cf /root/LicenceEole.txt
# eole@ac-dijon.fr
#
# serveurs.py
#
# pages web de gestion des serveurs
#
###########################################################################
from twisted.web import static
from twisted.web.resource import Resource
from twisted.internet import defer
from zephir.web.template.design import Design, proxy, get_user

import zephir.web.config as config
from zephir.utils.creolewrap import ZephirDict
from zephir.web.config import Navigation
from zephir.web.html.erreur import *

from zephir.eolerpclib import xmlrpclib
from zephir.lib_zephir import save_modes
from html import escape as html_escape
import apt_pkg
import time,os,base64,io,traceback, json
from configparser import ConfigParser, Error as CfgpError
from zephir.rapports import rapport
from zephir.web.html.dicos import gen_liste_dicos, gen_available_dicos
from functools import cmp_to_key


def escape(value):
    if isinstance(value, bytes):
        return value.decode()
    return value


class Serveur(Design):
    """Ressource conteneur serveur
    """
    def __init__(self, *args, **kwargs):
        self.entites = {
            'add':AddServeur
            ,'add2':AddServeur2
            ,'add3':AddServeur3
            ,'get':GetServeur
            ,'aff':AffServeur
            ,'del':DelServeur
            ,'deleted':DeletedServeur
            ,'get_dico':GetDico
            ,'edit':EditServeurDesc
            ,'edit2':EditServeurIns
            ,'action':ActionServeur
            ,'send_dico':ActionDico
            ,'conf':ActionConf
            ,'exec_script':ActionScript
            ,'save':ActionSave
            ,'maj':ActionMaj
            ,'download_upgrade':ActionUpgrade
            ,'reboot':ActionReboot
            ,'reconfigure':ActionReconfigure
            ,'service_restart':ActionServiceRestart
            ,'unlock':ActionUnlock
            ,'maj_client':ActionMajClient
            ,'param_groupe':ActionParamGroupe
            ,'regen_cert':ActionRegenCert
            ,'modif_param':ModGroupParam
            ,'etat':EtatServeur
            ,'log':LogServeur
            ,'purge':PurgeLogs
            ,'mod_locks':ModLocks
            ,'set_locks':SetLocks
            ,'perso':PersoServeur
            ,'sphynx_vpn':ConfVpn
            ,'sphynx_del':DelVpn
            ,'edit_fichier':EditFichier
            ,'save_file':SaveFile
            ,'migrate_conf':MigrateConf
            ,'rapport':RapportServeur
            ,'groupe':GroupeServeur
            ,'groupe_vars':GroupeVariables
            ,'remove_groupe':RemoveGroupe
            ,'save_groupe':SaveGroupe
            ,'extend_groupe':ExtendGroupe
            ,'del_groupe':DelGroupe
            ,'aff_groupe':AffGroupe
            ,'user_groupe':UserGroupe
            ,'detail_groupe':DetailGroupe
            ,'mod_usergroups':ModUserGroups
            ,'groupe_crit':GroupeCriteres
            ,'rech':RechServeur
            ,'agents':Agents
            ,'uucp':Uucp
            ,'purge_uucp':PurgeUucp
            ,'aff_alertes':AfficheAlertes
            ,'aff_migration':AfficheMigration
            ,'info_maj':InfoMaj
            ,'save_perms':SavePerms
            ,'del_perms':DeletePerms
            ,'copy_perms':CopyPerms
            ,'groupe_perms':GroupePerms
            ,'revert_serv':RevertServ
            ,'ldap_repl':ConfRepl
            ,'del_repl':DelRepl
            }
        super().__init__(*args, **kwargs)

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        name = name.decode()
        if name in self.entites:
            return self.entites[name]()
        return self

    def wmfactory_title(self,request):
        return "Gestion des serveurs"

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html").menu()

    def wmfactory_content(self, request):
        return """
        <h1>Gestion des serveurs</h1>
        <p>
        <a href="/serveur/rech">Rechercher un serveur</a><br/>
        <a href="/serveur/groupe">Sélectionner et enregistrer un groupe de serveurs</a><br/>
        <a href="/serveur/user_groupe">Gérer les groupes enregistrés de serveurs</a><br />
        <a href="/serveur/aff_alertes">Liste des serveurs en alertes</a><br/>
        <a href="/serveur/aff_migration">Suivre la migration</a>
        </p>"""


class RechServeur(Design):
    """Ressource conteneur serveur
    """
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"rech" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Recherche d'un serveur"

    def wmfactory_content(self, request):
        return """
<h1>Rechercher un serveur</h1><br/>
<FORM METHOD="POST" ACTION="/serveur/etat">
Identifiant du serveur&nbsp;<input type="text" size="10" name="id" value=""/><INPUT type=SUBMIT value="Ok">
"""

class RapportServeur(Design):
    """Liste des serveurs d'un établissement en PDF
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"rapport" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Rapport Serveurs d'un établissement"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                self.rne = escape(request.args[b'rne'][0])
                assert not self.rne == ""
            except Exception:
                raise FrontendError("identifiant")
            try:
                nom_pdf = rapport.Rapport(proxy(request),self.rne).pdf("serveurs_etab","'"+self.rne+"'")
                self.content = """<p>
                <a href="/tmp/%s">Télécharger le rapport <img border="0" src="/images/pdf.png"></a><br/>
                </p><p>
                <a href="javascript:history.back()">Retour</a><br/>
                </p>""" % os.path.basename(nom_pdf)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError

        except (FrontendError, BackendError) as e:
            self.content = e

        return self.content

class AddServeur(Design):
    """Ajout d'un serveur **page 1**
    saisie du module
    """
    isLeaf = True

    def _list_modules(self,request):
        try:
            modules = backend(proxy(request).modules.get_module())
        except BackendError:
            raise BackendError
        return pretty_select_module(modules)

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"add" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Ajout d'un serveur"

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html#section3").menu()

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                rne = escape(request.args[b'rne'][0])
            except Exception:
                raise FrontendError('rne')
            self.content = """<h1>Ajout d'un serveur</h1><h2>Choisissez le module du nouveau serveur</h2>
            <FORM METHOD=POST ACTION="add2">
            <table cellpadding="15" cellspacing="0" align="center" valign="middle" >
            <tr><td><select name= "module"> %s </select></td><td><INPUT type=SUBMIT value="Ok"></td></tr>
            <input name="rne" type="hidden" value="%s">
            </table>
            </form>
            """ % (self._list_modules(request), rne)

        except xmlrpclib.ProtocolError:
            self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

def valid_serveur(edit=False):
    """formulaire javascript pour valider la saisie/l'édition d'un serveur
    """
    script = """<script LANGUAGE="JavaScript"><!--
function is_numeric(obj)
{
    var charpos = obj.value.search("[^0-9]");
    if(obj.value.length > 0 &&  charpos >= 0)
    {
        strError = obj.name+": valeur numérique";
        alert(strError);
        return false;
    }
    else
    {
        return true;
    }
};
function validfields(form)
{
    //getting the values:
    if (form.libelle.value.length > 150)
    {
        alert("libelle : 150 caractères maximum !");
        form.libelle.focus();
        return false;
    }
    if (form.processeur.value.length > 100)
    {
        alert("processeur : 100 caractères maximum !");
        form.processeur.focus();
        return false;
    }
    if (form.disque_dur.value.length > 100)
    {
        alert("disque : 100 caractères maximum !");
        form.disque_dur.focus();
        return false;
    }
    if(!is_numeric(form.jour))
    {
        form.jour.focus();
        return false;
    }
    if (form.jour.value.length > 2)
    {
        alert("jour : 2 caractères maximum !");
        form.jour.focus();
        return false;
    }
    if(!is_numeric(form.mois))
    {
        form.mois.focus();
        return false;
    }
    if (form.mois.value.length > 2)
    {
        alert("mois : 2 caractères maximum !");
        form.mois.focus();
        return false;
    }
    if(!is_numeric(form.annee))
    {
        form.annee.focus();
        return false;
    }
    if (form.annee.value.length > 4)
    {
        alert("année : 4 caractères maximum !");
        form.annee.focus();
        return false;
    }
    if (form.installateur.value.length > 50)
    {
        alert("installateur : 50 caractères maximum !");
        form.installateur.focus();
        return false;
    }
    if (form.tel.value.length > 20)
    {
        alert("telephone : 20 caractères maximum !");
        form.tel.focus();
        return false;
    }"""
    if not edit:
        script +="""
    if (form.rne.value.length > 8)
    {
        alert("RNE : 8 caractères maximum !");
        form.rne.focus();
        return false;
    }"""
    script +="""
    if(!is_numeric(form.timeout))
    {
        form.timeout.focus();
        return false;
    }
    return true;
}
//--></script>"""
    return script


class AddServeur2(Design):
    """Ajout d'un nouveau serveur **page 2**
    saisie des informations détaillées serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'add2':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Ajout d'un serveur"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html#section=3").menu()

    def _dump_html(self, variantes):
        l = []
        for variante in variantes:
            if variante['libelle'] == 'standard':
                selected = ' SELECTED="selected"'
            else:
                selected = ""
            s = """<OPTION VALUE="%s"%s>%s</OPTION>""" % (variante['id'], selected, variante['libelle'])
            l.append(s)
        return "\n".join(l)

    def _get_variantes(self, request, module):
        try:
            variantes = backend(proxy(request).modules.get_variante())
        except BackendError:
            raise BackendError
        return [ var for var in variantes if var['module'] == module ]

    def _group_select(self,request):
        try:
            groupes = backend(proxy(request).serveurs.get_groups())
            data = ["""<select name=groupe>"""]
            data.append("""    <OPTION VALUE="" selected="selected"></OPTION>""")
            for groupe in groupes:
                data.append('    <OPTION VALUE="%s">%s</OPTION>' % (groupe[0],groupe[1]))
            data.append("""</select>""")
            return "\n".join(data)
        except BackendError:
            raise BackendError

    def renderView(self, request):
        try:
            try:
                rne = escape(request.args[b'rne'][0])
                module = int(escape(request.args[b'module'][0]))
            except Exception:
                raise FrontendError()

            self.content ="""
            %s
            <h1>Ajout d'un serveur</h1>
            <h2>Informations sur le serveur</h2>
            <form method="POST" action="add3"  onSubmit="return validfields(this)">
            <table cellpadding="5" cellspacing="0" align="center" valign="middle">
            <tr><td>Descriptif *</td><td>
            <input type="text" size="30" name="libelle" value=""></td></tr>
            <tr><td>Matériel</td><td>
            <input type="text" size="30" name="materiel" value=""></td></tr>
            <tr><td>Processeur</td><td>
            <input type="text" size="30" name="processeur" value=""></td></tr>
            <tr><td>Disque dur</td><td>
            <input type="text" size="30" name="disque_dur" value=""></td></tr>
            <tr><td>Date d'installation *</td><td>
            <input type="text" size="2" name="jour" value="%s">
            <input type="text" size="2" name="mois" value="%s">
            <input type="text" size="4" name="annee" value="%s"></td></tr>
            <tr><td>Installateur</td><td>
            <input type="text" size="30" name="installateur" value=""></td></tr>
            <tr><td>Téléphone</td><td>
            <input type="text" size="20" name="tel" value=""></td></tr>
            <input type="hidden" name="rne" value="%s">
            <input type="hidden" name="module" value="%s">
            <tr><td>Remarques</td><td><textarea rows="3" cols="30"
            name="remarques"></textarea></td></tr>
            <tr><td>Délai entre 2 contacts *</td><td><input type="text" size="6" name="timeout" value="%s">minutes</td></tr>
            <tr><td>Variante *</td><td>
            <select name= "variante"> %s </select>
            </td></tr><tr><td>Ajouter à un groupe</td><td>%s</td></tr><tr>
            <td align="center"><INPUT type='SUBMIT' value='Ok'></td>
            <td align="center"><input name="initialiser" value="Initialiser" type="reset"/></td>
            </tr>
            </table>
            <p><a href="/etab/get?rne=%s#fin-liste">Retour à l'établissement</a></p>
            </form>""" % (valid_serveur(), str(time.gmtime()[2]),str(time.gmtime()[1]),str(time.gmtime()[0]),rne,module,config.DEFAULT_TIMEOUT, self._dump_html(self._get_variantes(request,module)),self._group_select(request),rne)

        except xmlrpclib.ProtocolError:
            self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class AddServeur3(Design):
    """Ajout d'un nouveau serveur -- page 3
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'add3':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Ajout de Serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                materiel = escape(request.args[b'materiel'][0])
            except:
                materiel = ""
            try:
                processeur = escape(request.args[b'processeur'][0])
            except:
                processeur = ""
            try:
                disque = escape(request.args[b'disque_dur'][0])
            except:
                disque = ""
            try:
                tel = escape(request.args[b'tel'][0])
            except:
                tel = ""
            try:
                remarques = escape(request.args[b'remarques'][0])
            except:
                remarques = ""
            try:
                installateur = escape(request.args[b'installateur'][0])
            except:
                installateur = ""
            try:
                groupe = escape(request.args[b'groupe'][0])
                groupe = int(groupe)
            except:
                groupe = -1
            try:
                rne = escape(request.args[b'rne'][0])
                module = escape(request.args[b'module'][0])
                variante = escape(request.args[b'variante'][0])
                libelle = escape(request.args[b'libelle'][0])
                date = '/'.join([escape(request.args[b'jour'][0]),escape(request.args[b'mois'][0]),escape(request.args[b'annee'][0])])
                timeout = escape(request.args[b'timeout'][0])
                assert date
                assert timeout
            except Exception:
                raise FrontendError
            try:
                assert rne
            except AssertionError:
                raise FrontendError("identifiant")
            try:
                assert variante
            except AssertionError:
                raise FrontEnderror("variante")
            try:
                assert libelle
            except AssertionError:
                raise FrontendError("libellé")

            # appel au backend pour l'insertion du serveur dans la base
            try:
                try:
                    timeout = int(timeout)
                except:
                    timeout = 0

                id_serveur = backend(proxy(request).serveurs.add_serveur(
                    u(rne), u(libelle), u(materiel), u(processeur), u(disque),
                    u(date), u(installateur), u(tel), u(remarques), module,
                    module, int(timeout) * 60, variante, "", groupe))

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except DatabaseError as e:
                raise BackendError("Il est possible que le format de date ne soit pas correct<br/>")
            except:
                raise BackendError

            self.content = """<p><span id="message">Ajout du serveur %s terminé</span></p>
            <p><a href="/serveur/aff?id=%s">Aller au descriptif de ce serveur</a></p>
            <p><a href="/etab/get?rne=%s#fin-liste">Retour à la page de l'établissement de ce serveur</a></p>
            <!-- a href=FIXME>Installer un service sur le serveur</a -->
            """ % (libelle, id_serveur, rne)

        except Exception as e:
            self.content = e

        return self.content


class GetServeur(Design):
    """Affichage de la liste des serveurs pour un établissement
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'get':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Liste des serveurs"

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html").menu()

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self,liste):
        """Affichage de la liste des serveurs pour un établissement
        """
        l = []
        l.append("""
            <table cellpadding="1" cellspacing="2" border="1">
            <tr><td>id</td><td>libellé</td><td>module</td></tr>
        """)
        for d in liste:
            l.append("""
            <tr><td><a href="aff?id=%(id)s">%(id)s</a></td><td>%(libelle)s</td><td>%(module_actuel)s</td></tr>
            """ % d)

        l.append("""</table><p>
        <a href="add2?rne=%(rne)s">Ajouter un serveur</a><br>
        <a href="rapport?rne=%(rne)s">Générer un rapport <img border="0" src="/images/pdf.png"></a><br>
        <a href="/etab/get?rne=%(rne)s">Retour à l'établissement</a></p>""" % d)

        return "".join(l)

    def _get_module(self,serveur_list,mod_infos):
        """Filtre les clefs des modules
        """
        for serveur in serveur_list:
            if serveur['module_initial'] in mod_infos:
                serveur['module_initial'] = mod_infos[serveur['module_initial']][0]
            else:
                serveur['module_initial'] = "module non autorisé"
            serveur['module_actuel'] = mod_infos[serveur['module_actuel']][0]

        return serveur_list

    def _get_variante(self,serveur,libelle):
        """Filtre les clefs des variantes
        """
        for s in serveur:
            s['variante'] = libelle[s['variante']]
        return serveur

    def renderView(self, request):
        try:
            try:
                self.rne = escape(request.args[b'rne'][0])
                assert self.rne
            except Exception:
                raise FrontendError("identifiant")
            # récupération des serveurs
            try:
                liste = backend(proxy(request).serveurs.serveurs_etab(u(self.rne)))
                liste = self._get_module(liste,self.get_module(proxy(request)))
                liste = self._get_variante(liste,self.get_variante(proxy(request)))
                if liste != [] :
                    self.content = """<p>Liste des serveurs pour l'établissement <a href="/etab/get?rne=%s">%s</a>
                    <br/></p>%s""" % (self.rne,self.rne, self._dump_html(liste))
                else :
                    self.content = """
                    <p>Cet établissement ne possède pas encore de serveur installé<br/>
                    <a href="javascript:history.back()">Retour</a></p>
                    """
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class AffServeur(Design):
    """Affichage du détail d'un serveur pour un établissement
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'aff':
            return self
        return Serveur()

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html").menu()

    def wmfactory_title(self,request):
        return "Détail du serveur"

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self,d,id_groupe):
        """Affichage du détail d'un serveur
        """
        s = """
        <table id="tab_description" cellpadding="1" cellspacing="2">
        <tr><td>Identifiant</td><td>%(id)s</td></tr>
        <tr><td>Libellé</td><td>%(libelle)s</td></tr>
        <tr><td>Matériel</td><td>%(materiel)s</td></tr>
        <tr><td>Processeur</td><td>%(processeur)s</td></tr>
        <tr><td>Disque dur</td><td>%(disque_dur)s</td></tr>
        <tr><td>Date d'installation</td><td>%(date_install)s</td></tr>
        <tr><td>Installateur</td><td>%(installateur)s</td></tr>
        <tr><td>Téléphone</td><td>%(tel)s</td></tr>
        <tr><td>Remarques</td><td>%(remarques)s</td></tr>
        <tr><td>Délai de connexion (minutes)</td><td>%(timeout)s</td></tr>
        <tr><td>Alertes désactivées</td><td>%(no_alert)s</td></tr>
        <tr><td>Module initial</td><td>%(module_initial)s</td></tr>
        <tr><td>Module actuel</td><td><table border="0" cellspacing="0" cellpadding="0"><tr><td>%(module_actuel)s</td><td>%(backup)s</td></tr></table></td></tr>
        <tr><td>Variante</td><td>%(variante)s</td></tr>
        </table>
        <p><a href="edit?id=%(id)s&rne=%(rne)s">Modifier</a> / <a href="del?id=%(id)s&rne=%(rne)s">Supprimer</a> / <a href="action?id=%(id)s">Actions</a> / <a href="etat?id=%(id)s""" % d
        if id_groupe:
            s += """&id_groupe=%s""" % id_groupe
        s += """">État actuel</a></p>
        <p><a href="/etab/get?rne=%(rne)s#fin-liste">Retour à l'établissement</a></p>
        """ % d

        return s


    def _get_module(self,serveur_list,mod_infos):
        """Filtre les clefs des modules
        """
        for serveur in serveur_list:
            for s_mod in ('module_initial','module_actuel'):
                if serveur[s_mod] in mod_infos:
                    serveur[s_mod] = mod_infos[serveur[s_mod]][0]
                else:
                    serveur[s_mod] = "Module non autorisé"
        return serveur_list

    def _get_variante(self,serveur,libelle):
        """Filtre les clefs des variantes
        """
        for s in serveur:
            if s['variante'] in libelle:
                s['variante'] = libelle[s['variante']]
            else:
                s['variante'] = "Variante non autorisée"
        return serveur

    def renderView(self, request):
        try:
            id_groupe = None
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                pass
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            try:
                liste = backend(proxy(request).serveurs.get_serveur(id_serveur))
                liste = self._get_module(liste,self.get_module(proxy(request)))
                liste = self._get_variante(liste,self.get_variante(proxy(request)))
                if liste != []:
                    d = liste[0]
                    try:
                        d['timeout'] = int(d['timeout']) / 60
                    except:
                        d['timeout'] = ""
                    # désactivation des alertes
                    if str(d.get('no_alert',0)) == '1':
                        d['no_alert'] = '<font color="red">oui</font>'
                    else:
                        d['no_alert'] = 'non'
                    # on supprime les infos d'heures, minutes et secondes (qui sont toujours à 0)
                    # et on inverse l'ordre des champs (format français).
                    date_install = str(d['date_install']).split()[0]
                    date_install = date_install.split('-')
                    date_install.reverse()
                    d['date_install'] = "-".join(date_install)
                    # si disponible, bouton pour retour en arrière près migration
                    d['backup'] = ""
                    if d['module_actuel'] != d['module_initial']:
                        backup = backend(proxy(request).serveurs.check_backup(id_serveur))
                        if backup == True:
                            d['backup'] = """<FORM METHOD="POST" ACTION="revert_serv">
                                          <INPUT type="hidden" name="id" value="%(id)s"/>
                                          <INPUT type="SUBMIT" value="Retour au module précédent">
                                          </FORM>""" % d

                    self.content = """<h1>Descriptif du serveur %s</h1>
                    <h2>Serveur n° %s de l'établissement %s</h2>
                    <br/></p>%s""" % (d['libelle'], id_serveur, d['rne'], self._dump_html(d,id_groupe) )
                    # si une sélection de serveurs est stockée dans la session, on permet son affichage
                    if id_groupe:
                        self.content += """<a href="/serveur/aff_groupe?id_groupe=%s">Retour au groupe de serveurs</a><br><br>""" % id_groupe
                    else:
                        try:
                            groupe=request.getSession().groupe_serveur
                        except:
                            pass
                        else:
                            self.content += """<a href="/serveur/aff_groupe">Retour au groupe de serveurs</a><br><br>"""
                else:
                    self.content = """<p>Ce serveur est inconnu dans la base</p><br>
                    <a href="javascript:history.back()">Retour</a><br/>"""

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                traceback.print_exc()
                raise BackendError

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class EditServeurDesc(Design):
    """Modification d'un serveur, formulaire de renseignements
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"edit" :
            return self
        return Serveur()

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html").menu()

    def wmfactory_title(self,request):
        return "Édition de Serveur"

    def wmfactory_content(self, request):
        return self.content

    def _noalert_html(self, no_alert):
        """formatage de la liste de choix
        """
        sel_0 = sel_1 = ""
        if str(no_alert) == '1':
            sel_1 = ' selected="selected"'
        else:
            sel_0 = ' selected="selected"'
        data = """<option value=0 %s>Non</option>""" % sel_0
        data += """<option value=1 %s>Oui</option>""" % sel_1
        return data

    def _variante_html(self, selected, id_module, request) :
        """formatage de la liste variantes
        """
        variante = self.get_variante_module(id_module,proxy(request))
        l = []
        for v in list(variante.items()):
            value, libelle = v
            if selected == value:
                s = """<option value="%s" selected="selected">%s</option>""" % (value, libelle)
            else:
                s = """<option value="%s">%s</option>""" % (value, libelle)
            l.append(s)
        return "".join(l)

    def _module_html(self, mod_orig, var_orig, modules, allowed_edits, request):
        """formatage de la liste des modules si disponible
        """
        l = []
        content = ["""<script language="JavaScript">
        var variantes = new Object();"""]
        allowed_version = modules[mod_orig][1]
        for id_mod, mod_info in list(modules.items()):
            libelle, version = mod_info
            mod_name = libelle[:libelle.rindex('-')]
            if version == allowed_version and mod_name in allowed_edits:
                if mod_orig == id_mod:
                    s = """<option value="%s" selected="selected">%s</option>""" % (id_mod, libelle)
                else:
                    s = """<option value="%s">%s</option>""" % (id_mod, libelle)
                content.append("""variantes['%s'] = new Array();""" % (id_mod))
                variantes = self.get_variante_module(id_mod,proxy(request))
                for id_var, libel_var in list(variantes.items()):
                    selected = 'false'
                    if (int(id_mod) == int(mod_orig) and int(id_var) == int(var_orig)) or (int(id_mod) != int(mod_orig) and libel_var == 'standard'):
                        selected = 'true'
                    content.append("""variantes['%s'].push(new Option('%s', '%s', false, %s));""" % (id_mod,libel_var,str(id_var),selected))
                l.append(s)

        content.append("""function edit_module ()
        {
            //getting the values:
            var module = document.forms['edit_form'].module_actuel.value;
            if ( module != '%s')
            {
                document.forms['edit_form'].variante.options.length=0;
                for (var i=0; i < variantes[module].length; i++)
                {
                    document.forms['edit_form'].variante.options.add(variantes[module][i]);
                }
            }
            else
            {
                document.forms['edit_form'].variante.disabled = false;
            }
        }
        </script>
        <select name="module_actuel" onChange="edit_module()">%s</select>""" % (str(selected), "".join(l)))
        return "\n".join(content)

    def _dump_html(self,d):
        """Formulaire d'edition du serveur
        """
        return """
%(valid_form)s
<h1>Édition du serveur %(libelle)s</h1>
<h2>Serveur n° %(id)s de l'établissement %(rne)s</h2>
<form name="edit_form" method=POST action="edit2" onSubmit="return validfields(this)">
<table cellpadding="1" cellspacing="2">
<tr><td>Identifiant</td><td>%(id)s
<input type="hidden" name="id" value="%(id)s">
<input type="hidden" name="rne" value="%(rne)s">
</td></tr>
<tr><td>Libellé</td><td>
<input type=text size=8 name="libelle" value="%(libelle)s"></td></tr>
</td></tr>
<tr><td>Matériel</td><td>
<input type=text size=8 name="materiel" value="%(materiel)s"></td></tr>
</td></tr>
<tr><td>Processeur</td><td>
<input type=text size=8 name="processeur" value="%(processeur)s"></td></tr>
</td></tr>
<tr><td>Disque dur</td><td>
<input type=text size=8 name="disque_dur" value="%(disque_dur)s"></td></tr>
</td></tr>
<tr><td>Date installation</td><td>
<input type=text size=2 name="jour" value="%(jour)s">
<input type=text size=2 name="mois" value="%(mois)s">
<input type=text size=4 name="annee" value="%(annee)s"></td></tr>
</td></tr>
<tr><td>Installateur</td><td>
<input type=text size=8 name="installateur" value="%(installateur)s"></td></tr>
</td></tr>
<tr><td>Téléphone</td><td>
<input type=text size=8 name="tel" value="%(tel)s"></td></tr>
</td></tr>
<tr><td>Delai maximum entre 2 contacts</td><td>
<input type=text size=6 name="timeout" value="%(timeout)s"></td></tr>
</td></tr>
<tr><td>Remarques</td><td>
<input type=text size=8 name="remarques" value="%(remarques)s"></td></tr>
</td></tr>
<tr><td>Module initial</td><td>
%(module_initial)s</td></tr>
<tr><td>Module actuel</td><td>
%(module_actuel)s</td></tr>
<tr><td>Variante</td><td>
<select name="variante">%(variante)s</select>
</td></tr>
<tr><td>Désactiver les alertes pour ce serveur</td><td>
<select name="no_alert">%(no_alert)s</select>
</td></tr>
<tr><td align="center"><INPUT type=SUBMIT value="Ok"></td>
<td>
<td align="center"><input name="initialiser" value="Initialiser" type="reset" />
</td></tr>
</tr></table>
</form><p><a href ="/serveur/aff?id=%(id)s">Retour au descriptif du serveur</a></p>
""" % d

    def renderView(self, request):
        # analyse de la requète
        try:
            try:
                id = escape(request.args[b'id'][0])
                assert id
                rne = escape(request.args[b'rne'][0])
                assert rne
            except Exception:
                raise FrontendError("identifiant")

            # réponse du backend
            try:
                resultat = backend(proxy(request).serveurs.get_serveur(id))
                d = resultat[0]
                d['rne'] = rne
                # récupération des libellés et stockage des clefs
                d['id_module_initial'] = d['module_initial']
                d['id_module_actuel'] = d['module_actuel']
                modules = self.get_module(proxy(request))
                if d['module_initial'] in modules:
                    d['module_initial'] = modules[d['module_initial']][0]
                else:
                    d['module_initial'] = "Module non autorisé"
                d['module_actuel'] = modules[d['module_actuel']][0]
                try:
                    d['timeout'] = int(int(d['timeout']) / 60)
                except:
                    d['timeout'] = ""
                if d['id_module_initial'] in modules:
                    # le module initial n'est pas interdit à l'utilisateur
                    if modules[d['id_module_initial']][1] in config.allowed_mod_edits:
                        # il existe des modules editables pour cette version des modules (ex : amon/amonecole sur eole 2.2)
                        mod_name = d['module_actuel'][:d['module_actuel'].rindex('-')]
                        # on regarde si l'édition de ce module est autorisée
                        if mod_name in config.allowed_mod_edits[modules[d['id_module_initial']][1]]:
                            allowed_edits = config.allowed_mod_edits[modules[d['id_module_initial']][1]][mod_name]
                            # amon/amonecole : modification permise
                            allowed_edits.append(mod_name)
                            d['module_actuel'] = self._module_html(d['id_module_actuel'], d['variante'], modules, allowed_edits, request)
                # mise en forme de la date
                d['date_install']=str(d['date_install']).split()[0]
                d['jour'] = d['date_install'].split('-')[2]
                d['mois'] = d['date_install'].split('-')[1]
                d['annee'] = d['date_install'].split('-')[0]
                d['variante'] = self._variante_html(d['variante'],d['id_module_actuel'],request)
                d['no_alert'] = self._noalert_html(d['no_alert'])
                d['valid_form'] = valid_serveur(edit=True)
                self.content = self._dump_html(d)

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                traceback.print_exc()
                raise BackendError

        except Exception as e:
            self.content = e

        return self.content


class EditServeurIns(Design):
    """Edition d'un serveur, update du backend
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"edit2" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Édition de Serveur"

    def wmfactory_content(self, request):
        return self.content


    def renderView(self, request):
        d = {}
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                rne = escape(request.args[b'rne'][0])
                d['libelle'] = escape(request.args[b'libelle'][0])
                d['materiel'] = escape(request.args[b'materiel'][0])
                d['processeur'] = escape(request.args[b'processeur'][0])
                d['disque_dur'] = escape(request.args[b'disque_dur'][0])
                d['date_install'] = "/".join([escape(request.args[b'jour'][0]),escape(request.args[b'mois'][0]),escape(request.args[b'annee'][0])])
                d['installateur'] = escape(request.args[b'installateur'][0])
                d['tel'] = escape(request.args[b'tel'][0])
                d['remarques'] = escape(request.args[b'remarques'][0])
                d['variante'] = escape(request.args[b'variante'][0])
                try:
                    d['timeout'] = int(escape(request.args[b'timeout'][0])) * 60
                except:
                    d['timeout'] = 0
                try:
                    d['no_alert'] = int(escape(request.args[b'no_alert'][0]))
                except:
                    d['no_alert'] = 0
            except Exception:
                raise FrontendError
            try:
                assert id_serveur
            except AssertionError:
                raise FrontendError("id")
            try:
                assert d['libelle']
            except AssertionError:
                raise FrontendError("libelle")
            try:
                assert d['variante']
            except AssertionError:
                raise FrontendError("variante")
            if b'module_actuel' in request.args:
                try:
                    d['module_actuel'] = int(escape(request.args[b'module_actuel'][0]))
                except:
                    pass
            # backend
            try:
                resultat = backend(proxy(request).serveurs.edit_serveur(id_serveur,u(d)))
                self.content = """
                <p>Le serveur %s a bien été modifié<br>
                <a href="/serveur/aff?id=%s">Retour à la description du serveur</a><br/>
                </p>
                """ % (id_serveur, id_serveur)

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError()

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class DelServeur(Design):
    """Confirmation de la suppression d'un serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"del" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression de Serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id = escape(request.args[b'id'][0])
                rne = escape(request.args[b'rne'][0])
            except Exception:
                raise FrontendError("id")
            try:
                assert id
            except AssertionError:
                raise FrontendError("id")

            self.content = """
            <p><span id="message">Voulez-vous vraiment supprimer le serveur %s ?</span></p>
            <p><a href="deleted?id=%s&rne=%s">Confirmer la suppression</a></p>
            <p><a href="get?rne=%s">Retour à la liste des serveurs de l'établissement</a></p>
            """ % (id,id,rne,rne)

        except xmlrpclib.ProtocolError:
            raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content


class DeletedServeur(Design):
    """Suppression effective d'un serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"deleted" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression d'un serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id = escape(request.args[b'id'][0])
                rne = escape(request.args[b'rne'][0])
            except Exception:
                raise FrontendError("id")
            try:
                assert id
            except AssertionError:
                raise FrontendError("id")
            try:
                backend(proxy(request).serveurs.del_serveur(id))
                # on supprime le serveur du groupe actuel de l'utilisateur si il en fait partie
                # FIXME : valable seulement pour l'utilisateur courant
                prefs = request.getSession()
                if hasattr(prefs, 'groupe_serveur'):
                    groupe_serv = prefs.groupe_serveur
                    for serv in groupe_serv:
                        if int(serv['id']) == int(id):
                            groupe_serv.remove(serv)
                            break
                    setattr(prefs,'groupe_serveur',groupe_serv)
                self.content = """
                <p><span id="message">Le serveur a bien été supprimé</span></p>
                <p><a href="/etab/get?rne=%s#fin-liste">Retour à la liste des serveurs de l'établissement</a></p>
                """ % rne

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError

        except (FrontendError,BackendError) as e:
                self.content = e

        return self.content



class ActionServeur(Design):
    """Actions sur un serveur, formulaire de renseignements
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"action" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Actions sur un serveur"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation(aide="page8.html").menu()

    def _data_select_configure(self, id_serveur, request):
        s_data = ["""<SELECT NAME="content">"""]
        for tag, data in list(config.data_files.items()):
            s_data.append("""<OPTION value=%s>%s</OPTION>""" % (str(tag), data[0]))
        s_data.append("""</SELECT>""")
        return "\n".join(s_data)

    def _data_select_save(self, id_serveur, request):
        s_data = ["""<SELECT NAME="mode">"""]
        for tag, descr in list(save_modes.items()):
            s_data.append("""<OPTION value=%s>%s</OPTION>""" % (str(tag), descr))
        s_data.append("""</SELECT>""")
        return "\n".join(s_data)

    def _delay_option(self, id_serveur, request):
        """retourne un input pour saisir un délai si le client est compatible
        """
        return """&nbsp;délai <INPUT type="text" size="2" name="delay" value="0"/>heures"""

    def _dump_html(self, id_serveur, id_groupe, infos, request, module_version, options_version, warn_dicos):
        """Formulaire d'edition d'une action
        """
        if id_serveur == str(-1):
            s = """<h1>Actions sur le groupe de serveurs</h1><br>
            <table cellpadding="4" cellspacing="2" align="center" valign="middle" >"""
        else:
            s = """<h1>Actions sur le serveur <b>%s</b>&nbsp;(%s - %s - %s) :</h1><br>
            <table cellpadding="4" cellspacing="2" align="center" valign="middle" >""" % (infos['libelle'],infos['rne'],self.get_module(proxy(request))[infos['module_actuel']][0],id_serveur)
        delay_option = self._delay_option(id_serveur, request)
        if warn_dicos:
            if id_serveur == str(-1):
                serv_label = "sur au moins un des serveurs"
            else:
                serv_label = "sur ce serveur"
            # message d'avertissement si dictionnaires non synchronisés
            msg_dicos = """<tr><td colspan="2"><font color="red">Attention</font>, des dictionnaires ont été installés manuellement %s,<br/>
 l'envoi de configuration est susceptible d'écraser des modifications locales</td><td></td><td></td></tr>""" % serv_label
        else:
            msg_dicos = ""
        s = s + """
        <script language="JavaScript"><!--
        function check_delay(action_form)
        {
            //verify that delay is a positive integer
            input_delay = action_form.delay;
            if ( input_delay != undefined ) {
                var maj_delay = input_delay.value;
                if (parseInt(maj_delay) >= 0)
                {
                    return true;
                } else {
                    alert('Le délai doit être un nombre entier positif');
                    input_delay.focus();
                }
            }
            return false;
        }
        function set_maj_options ()
        {
            //getting the values:
            if check_delay(document.forms['maj_form'])
            {
                var maj_delay = document.forms['maj_form'].delay.value;
                if (eval(maj_delay) > 0)
                {
                    document.forms['maj_form'].check.checked = true;
                    document.forms['maj_form'].check.disabled = true;
                }
                else
                {
                    document.forms['maj_form'].check.disabled = false;
                }
                return true;
            }
            return false;
        }
        //--></script>
        <FORM METHOD=POST ACTION="conf">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan = "2"><INPUT type=SUBMIT value="Envoyer la configuration au serveur">%s
        <input type="checkbox" name="check" checked/>lancer reconfigure</td><td></td><td></td></tr>%s</FORM>
        <FORM METHOD=POST ACTION="save">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td><INPUT type=SUBMIT value="Sauvegarder l'état actuel du serveur">%s</td></tr></form>
        <FORM METHOD=POST NAME="maj_form" ACTION="maj">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan="2"><INPUT type=SUBMIT value="Mettre à jour le serveur">
        délai <INPUT onBlur="javascript:set_maj_options()" type="text" size="2" name="delay" value="0"/>heures
        <input type="checkbox" name="check" checked/>lancer reconfigure""" % \
                (id_serveur, self._data_select_configure(id_serveur, request), msg_dicos, id_serveur, self._data_select_save(id_serveur, request), id_serveur)
        if module_version >= 2 and module_version < 6:
            s = s + """<input type="checkbox" name="check_complete"/>forcer une mise à jour complète"""
        s = s + """</td><td></td><td></td></tr></FORM>"""
        if module_version >= 2:
            s = s + """
            <FORM METHOD=POST ACTION="regen_cert">
            <INPUT type="hidden" name="id" value="%s"/>
            <tr><td colspan = "2"><INPUT type=SUBMIT value="Regénérer la clé d'enregistrement SSH">
            <input type="checkbox" name="check_ssl" checked/>regénérer les certificats des applications webs</td>
            <td colspan="2"></td></tr></FORM>""" % id_serveur
        if options_version != []:
            s = s + """
            <FORM METHOD=POST ACTION="download_upgrade" onSubmit="check_delay(this);">
            <INPUT type="hidden" name="id" value="%s"/>
            <tr><td colspan="2"><INPUT type=SUBMIT value="Télécharger l'iso avant migration (Upgrade-Auto)">
            Version de destination<select name="version">%s</select>
            délai <INPUT type="text" size="2" name="delay" value="0"/>heures</td>
            <td></td><td></td></tr></FORM>""" % (id_serveur,options_version)
        s = s + """
        <FORM METHOD=POST ACTION="service_restart" onSubmit=check_delay(this);>
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan="2"><INPUT type=SUBMIT value="Redémarrer un service">
        nom du service&nbsp;<INPUT type="text" size="15" name="service" value=""/>%s</td>
        <td></td><td></td></tr></FORM>
        <FORM METHOD=POST ACTION="maj_client"><INPUT type="hidden" name="id" value="%s"/>
        <tr><td><INPUT type=SUBMIT value="Mettre à jour zephir_client"></td></tr>
        </FORM><FORM METHOD=POST ACTION="copy_perms">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan="2"><INPUT type=SUBMIT value="Ajouter les permissions d'un serveur">
        <INPUT type="text" name="id_src" value="" size="3">(n° serveur source)
        <input type="checkbox" name="keep" checked/>garder les droits existants</td></tr></FORM>
        """ % (id_serveur,delay_option,id_serveur,id_serveur)
        if id_groupe:
            groupe_input = """<INPUT type="hidden" name="id_groupe" value="%s"/>""" % id_groupe
        else:
            groupe_input = ""
        if id_serveur == str(-1):
            s = s + """<FORM METHOD=POST ACTION="groupe_perms">%s<tr><td>
            <INPUT type=SUBMIT value="Supprimer des permissions"></td></tr></FORM>""" % groupe_input
        s = s + """<FORM METHOD=POST ACTION="reboot" onSubmit="check_delay(this);">
        <INPUT type="hidden" name="id" value="%s"/>%s
        <tr><td colspan="2"><INPUT type=SUBMIT value="Redémarrer le serveur">%s
        </td><td></td><td></td></tr></FORM>
        <FORM METHOD=POST ACTION="reconfigure" onSubmit="check_delay(this);">
        <INPUT type="hidden" name="id" value="%s"/>%s
        <tr><td colspan="2"><INPUT type=SUBMIT value="Reconfigurer le serveur">%s
        </td><td></td><td></td></tr></FORM>""" \
        % (id_serveur, groupe_input, delay_option, id_serveur, groupe_input, delay_option)

        s = s + """<FORM METHOD=POST ACTION="exec_script">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan="2"><INPUT type=SUBMIT value="Exécuter un script sur le client">
        nom du script&nbsp;<INPUT type="text" name="script_name" value="" size="15">
        arguments&nbsp;<INPUT type="text" name="params" value="" size="15"></td></tr></FORM>
        <FORM METHOD=POST ACTION="purge_uucp">
        <INPUT type="hidden" name="id_serveur" value="%s"/>
        <tr><td><INPUT type=SUBMIT value="Annuler toutes les actions en attente"></td></tr></FORM>
        <FORM METHOD=POST ACTION="unlock">
        <INPUT type="hidden" name="id" value="%s"/>
        <tr><td colspan="2"><INPUT type=SUBMIT value="Demander la suppression des verrous zephir">
        </td><td></td><td></td></tr></FORM>
        <FORM METHOD=POST ACTION="mod_locks">
        <tr><td><INPUT type=SUBMIT value="Interdiction de fonctions"></td></tr>
        """ % (id_serveur,id_serveur,id_serveur)
        if id_serveur == str(-1):
            s = s + """<INPUT type="hidden" name="id_groupe" value="%s"/></FORM>
            </FORM><FORM METHOD=POST ACTION="param_groupe">
            <INPUT type="hidden" name="id" value="%s"/>""" % (id_groupe,id_serveur)
            if id_groupe:
                s = s + """<INPUT type="hidden" name="id_groupe" value="%s"/>""" % id_groupe
            s = s + """<tr><td><INPUT type=SUBMIT value="Modifier un paramètre sur le groupe"></td></tr></form>
            <FORM METHOD=POST ACTION="purge">
            <INPUT type="hidden" name="id" value="%s"/>
            <tr><td colspan="2"><table><tr><td><INPUT type=SUBMIT value="Purger les logs suivants"></td><td>
            <table cellpadding="2" cellspacing="0">antérieurs au
            <input type="text" size="2" name="jour" value="%s">
            <input type="text" size="2" name="mois" value="%s">
            <input type="text" size="4" name="annee" value="%s"></td><td>
            <input type="checkbox" name="check_action"/>logs des actions</td></tr><tr><td>
            <input type="checkbox" name="check_info"/>surveillance et infos diverses</td><td>
            <input type="checkbox" name="check_all"/>tous les types</td></tr></table>
            </td><tr></table></td></tr></form></table>
            <p align="center"><a href="/serveur/aff_groupe""" % (id_serveur, str(time.gmtime()[2]),str(time.gmtime()[1]),str(time.gmtime()[0]))
            if id_groupe:
                s = s + """?id_groupe=%s""" % id_groupe
            return s + """">Retour au groupe de serveurs</a></p>"""
        else:
            return s + """<INPUT type="hidden" name="id" value="%s"/></FORM>
            <FORM METHOD=POST ACTION="send_dico" enctype="multipart/form-data">
            <tr><td colspan="2"><INPUT type="hidden" name="id" value="%s">
            <INPUT type=SUBMIT value="Envoyer zephir.eol sur Zéphir">
            <INPUT NAME="dico" TYPE="file"/></td></tr></form>
            </table><p><a href="/serveur/aff?id=%s">Retour à la page du serveur</a>
            &nbsp;/&nbsp;<a href="/serveur/etat?id=%s">État du serveur</a></p>
            """% (id_serveur,id_serveur,id_serveur,id_serveur)

    def renderView(self, request):
        try:
            id_groupe = None
            infos = None
            module_infos = None
            warn_dicos = False
            # par défaut, on se considère sur un module 2.4.0
            module_version = 6
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                pass
            else:
                id_serveur = "-1"
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                if int(id_serveur) != -1:
                    infos = backend(proxy(request).serveurs.get_serveur(int(id_serveur)))[0]
                    module_infos = backend(proxy(request).modules.get_module(infos['module_actuel']))[0]
                    module_version = int(module_infos['version'])
                    module_lib = module_infos['libelle'][:module_infos['libelle'].rindex('-')]
                    try:
                        # dictionnaires installés manuellement sur le serveur ?
                        warn_dicos = backend(proxy(request).serveurs.get_status(int(id_serveur)))['dictpaqs_ok'][0] == 0
                    except:
                        traceback.print_exc()
            except Exception:
                if id_groupe is None:
                    raise FrontendError("identifiant du serveur")

            ## Recherche des modules de destination disponibles en Upgrade-Auto
            # si mode groupe, on cherche le serveur ayant la version la plus basse
            if infos is None:
                module_version = list(config.DISTRIBS.keys())[-1]
                modules = self.get_module(proxy(request))
                prefs = request.getSession()
                liste_serv = prefs.groupe_serveur
                try:
                    # dictionnaires installés manuellement sur un des serveurs ?
                    warn_dicos = backend(proxy(request).dicos.check_inactive([serv['id'] for serv in liste_serv]))
                except:
                    traceback.print_exc()
                for serv in liste_serv:
                    serv_version = modules[serv['module_actuel']][1]
                    if serv_version < module_version:
                        module_version = serv_version

            # calcul des versions permettant un préchargement des images d'une distribution supérieure
            options_version = []
            if config.get_next_dist(module_version) in config.DISTRIBS:
                # liste des distributions 'majeures' à partir de la version source
                major_dists = []
                dist_keys = config.dist_keys[config.dist_keys.index(module_version):]
                for dist in dist_keys:
                    dist_infos = config.DISTRIBS[dist]
                    if dist_infos[0] not in major_dists:
                        major_dists.append(dist_infos[0])
                if major_dists and len(major_dists) > 1:
                    if infos:
                        # limitation à la version suivante si un seul serveur
                        major_dists = major_dists[1]
                    else:
                        # groupe : versions supérieures au serveur ayant la plus petite version
                        major_dists = major_dists[1:]

                    for release, details in list(config.DISTRIBS.items()):
                        # download d'iso disponible seulement à partir de 2.5.0 (destination)
                        # version maintenues uniquement
                        if details[0] in major_dists and details[2] and release > 8:
                            options_version.append("""<OPTION VALUE="%s">eole-%s (%s)</OPTION>""" % (release, details[1], details[0]))
            self.content = self._dump_html(id_serveur,id_groupe,infos,request,module_version,options_version,warn_dicos)

        except Exception as e:
            traceback.print_exc()
            self.content = e

        return self.content

class ModLocks(Design):
    """choix des actions à bloquer sur les serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"mod_locks" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Choix des procédures verrouillées"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation().menu()

    def _dump_html(self, tags, id_serveur, id_groupe):
        """Formulaire de sélection des verrous
        """
        s = """<h1>Choix des procédures à bloquer</h1><br><br>
        <table cellpadding="15" cellspacing="8" align="center" valign="middle" >
        <FORM METHOD=POST ACTION="set_locks">
        <INPUT type="hidden" name="id" value="%s"/>""" % id_serveur
        if str(id_groupe) != "None":
            s+="""<INPUT type="hidden" name="id_groupe" value="%s"/>""" % id_groupe
        s+="""<tr><th bgcolor"#beccec" align="center" colspan="2">procédure</th><th bgcolor"#beccec" align="center">interdire</th><th bgcolor"#beccec" align="center">autoriser</th></tr>"""
        for tag in tags:
            s+="""<tr><td>%s</td><td><font %s>(%s)</font></td></td><td align="center"><input type="checkbox" name="check_%s"/></td><td align="center"><input type="checkbox" name="uncheck_%s"/></td></tr>""" % (tag[1],tag[2],tag[0],tag[0],tag[0])
        s+="""<tr><td colspan="4" align="center"><INPUT type=SUBMIT value="Valider"></td></tr>
        <tr><td colspan="4" align="center"><br><a href="/serveur/action?"""
        if str(id_groupe) != "None":
            s+="""id_groupe=%s""" % id_groupe
        else:
            s+="""id=%s""" % id_serveur
        s+='">retour à la page des actions</a></td></tr></table>'

        return s

    def renderView(self, request):
        try:
            id_groupe = None
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                pass
            else:
                id_serveur = "-1"
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                if id_groupe is None:
                    raise FrontendError("identifiant du serveur")
            try:
                # récupération de la liste des tags disponibles.
                tags=backend(proxy(request).serveurs.get_locks())
                if id_serveur != "-1":
                    tags_serveur = backend(proxy(request).serveurs.get_locks(int(id_serveur)))
                else:
                    tags_serveur = []

                liste_tags=[]
                for tag in tags:
                    if tag in tags_serveur:
                        liste_tags.append([tag[0],tag[1],' color="red"'])
                    else:
                        if id_groupe != None or id_serveur == -1:
                            liste_tags.append([tag[0],tag[1],''])
                        else:
                            liste_tags.append([tag[0],tag[1],' color="green"'])

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("Erreur lors de la recherche des verrous disponibles")

            self.content = self._dump_html(liste_tags,id_serveur,id_groupe)

        except Exception as e:
            self.content = e

        return self.content


class SetLocks(Design):
    """Mise en place effective des locks
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"set_locks":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Blocage de fonctions (groupe de serveurs)"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            id_groupe = None
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                try:
                    id_serveur = escape(request.args[b'id'][0])
                    assert id_serveur
                    options = "id=%s" % id_serveur
                except Exception:
                    raise FrontendError("identifiant du serveur")
            else:
                id_serveur = "-1"
                options = "id_groupe=%s" % id_groupe
            tags = []
            notags = []
            for var in request.args.keys():
                var = var.decode()
                if var.startswith('check_'):
                    tags.append(var[var.index('_')+1:])
                elif var.startswith('uncheck_'):
                    notags.append(var[var.index('_')+1:])
            try:
                if id_serveur == str(-1):
                    # cas d'un groupe
                    # on récupère les ids des serveurs
                    groupe_data=request.getSession().groupe_serveur
                    serveurs = []
                    for serveur in groupe_data:
                        serveurs.append(serveur['id'])
                    backend(proxy(request).serveurs.maj_locks(u(serveurs),u(tags),u(notags)))
                else:
                    backend(proxy(request).serveurs.maj_locks(u([id_serveur]),u(tags),u(notags)))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError("Erreur lors de la mise en place des verrous : %s " % str(e))

            self.content = """<p>Verrous mis à jour<br/>
            <a href="/serveur/action?%s">Retour à la page des actions</a><br/>
            </p>""" % options

        except Exception as e:
            self.content = e

        return self.content

class PurgeLogs(Design):
    """Suppression effective des logs d'un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"purge":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Purge des journaux systèmes (groupe de serveurs)"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id = escape(request.args[b'id'][0])
                #if id != -1:
                #    raise ValueError('possible sur un groupe de serveur uniquement')
            except Exception:
                raise FrontendError("id")
            try:
                date = [escape(request.args[b'jour'][0]),escape(request.args[b'mois'][0]),escape(request.args[b'annee'][0])]
                #date = "/".join([escape(request.args[b'jour'][0]),escape(request.args[b'mois'][0]),escape(request.args[b'annee'][0])])
                types={
                       'check_cmd':['COMMAND'],
                       'check_action':['CONFIGURE','RECONFIGURE','MAJ','SAUVEGARDE','INSTANCE','SERVICE_RESTART','UPGRADE','PERSO'],
                       'check_info':['INFO','SURVEILLANCE','RVP','LOCK','ZEPHIR','QUERY-MAJ'],
                       'check_all':['TOUT']
                      }
                liste_types = []
                for type_log in list(types.keys()):
                    if type_log.encode() in request.args:
                        liste_types.extend(types[type_log])

                # récupération des serveurs du groupe
                liste_serveurs = []
                for info_serveur in request.getSession().groupe_serveur:
                    liste_serveurs.append(info_serveur['id'])

                if liste_serveurs != [] and liste_types != []:
                    backend(proxy(request).serveurs.del_log(u(liste_serveurs),u(liste_types),u(date)))
                    self.content = """
                    <p>Les logs sélectionnés ont été purgés<br/></p>
                    <p><a href="javascript:history.back()">retour à la page précédente</a></p>
                    """
                else:
                    self.content = """
                    <p>le groupe de serveurs est vide ou aucun type de logs spécifié<br/></p>
                    <p><a href="javascript:history.back()">retour à la page précédente</a></p>
                    """

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError()

        except (FrontendError,BackendError) as e:
                self.content = e

        return self.content

class ActionParamGroupe(Design):
    """choix d'un paramètre à modifier (groupe)
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"param_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Choix du paramètre à modifier (groupe)"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation().menu()

    def _dump_html(self, id_groupe, params, liste_variantes):
        """Formulaire de sélection du paramètre
        """
        # liste de choix
        l = ['<select name="variable">']
        cles = list(params[0].keys())
        cles.sort()
        for param in cles:
            vals = params[0][param]
            l.append("""<OPTION VALUE="%s">%s (%s valeurs)</OPTION>""" % (param, param, len(vals)))
        l.append("</select>")

        s = """<h1>Paramètres modifiables pour ce groupe</h1>
        <table cellpadding="15" cellspacing="8" align="center" valign="middle" >
        <FORM METHOD=POST ACTION="modif_param">"""
        if id_groupe is not None:
            s+="""<INPUT type="hidden" name="id_groupe" value="%s"/>""" % str(id_groupe)
        else:
            s+="""<INPUT type="hidden" name="id_serveur" value="-1"/>"""
        s+="""<tr><td>Variables eole (zephir.eol)<br>&nbsp;nouvelle valeur</td><td>%s<br><INPUT type="text" name="valeur" value=""/><INPUT type=SUBMIT value="Changer la valeur"><br/><font color="blue">Pour les variables multivaluées (eole NG), utiliser | pour séparer les valeurs</td></tr></FORM>""" % "\n".join(l)

        s+="""<br><FORM METHOD=POST ACTION="modif_param">"""
        if id_groupe is not None:
            s+="""<INPUT type="hidden" name="id_groupe" value="%s"/>""" % str(id_groupe)
        else:
            s+="""<INPUT type="hidden" name="id_serveur" value="-1"/>"""
        s+="""<tr><td>Délai de connexion</td><td><INPUT type="text" name="timeout" value=""/><INPUT type=SUBMIT value="Changer le d&eacute;lai"></td></tr></FORM>"""
        s+="""<tr><td>Gestion des alertes</td><td align="left">"""
        for val, msg in ( ('1', 'Désactiver les alertes'), ('0', 'Réactiver les alertes') ):
            s+="""<FORM METHOD=POST ACTION="modif_param">"""
            if id_groupe is not None:
                s+="""<INPUT type="hidden" name="id_groupe" value="%s"/>""" % str(id_groupe)
            else:
                s+="""<INPUT type="hidden" name="id_serveur" value="-1"/>"""
            s+="""<INPUT type="hidden" name="no_alert" value="%s"/><INPUT type="SUBMIT" value="%s"/></FORM>""" % (val,msg)
        s+="""</td></tr>"""
        if liste_variantes != []:
            # si un seul module : liste des variantes
            s+="""<br><FORM METHOD=POST ACTION="modif_param">"""
            if id_groupe is not None:
                s+="""<INPUT type="hidden" name="id_groupe" value="%s"/>""" % str(id_groupe)
            else:
                s+="""<INPUT type="hidden" name="id_serveur" value="-1"/>"""
            l = ['<select name="variante">']
            for variante in liste_variantes:
                l.append("""<OPTION VALUE="%s">%s (%s)</OPTION>""" % (variante["id"],variante["libelle"],variante["id"]))
            l.append("</select>")
            s+="""<tr><td>Variante</td><td>%s""" % "\n".join(l)
            s+="""<INPUT type=SUBMIT value="Appliquer la variante"></td></tr></FORM>"""

        s+="""<br><tr><td colspan="3" align="center"><br><a href="/serveur/action?"""
        if id_groupe is not None:
            # id groupe si groupe enregistré
            s+="""id_groupe=%s""" % id_groupe
        else:
            # id_serveur = -1 si groupe en mémoire
            s+="""id=-1"""
        s+='">retour à la page des actions</a></td></tr></table>'

        return s

    def renderView(self, request):
        try:
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                id_groupe = None
                # groupe non enregistré dans la base

            # on cherche les serveurs dans la session
            liste_serveurs = []
            for info_serveur in request.getSession().groupe_serveur:
                liste_serveurs.append(info_serveur['id'])
            try:
                # récupération de la liste des paramètres communs
                params = backend(proxy(request).serveurs.get_groupe_vars(liste_serveurs,True))
                variantes = backend(proxy(request).modules.get_variante())
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            # except Exception,e:
            #     raise BackendError("Erreur lors de la recherche des variables disponibles<br>(%s)" % str(e))

            # si un seul module
            if len(params[1]) == 1:
                liste_variantes=[]
                # on cherche les variantes
                for variante in variantes:
                    if int(variante['module']) == int(params[1][0]):
                        liste_variantes.append(variante)
            else:
                liste_variantes = []

            self.content = self._dump_html(id_groupe,params,liste_variantes)

        except Exception as e:
            self.content = e

        return self.content

class ModGroupParam(Design):
    """Modifie un paramètre sur un groupe de serveurs
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'modif_param':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Modification d'un paramètre"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
                id_serveur = -1
            except:
                id_groupe = None
                try:
                    id_serveur = escape(request.args[b'id_serveur'][0])
                except:
                    raise FrontendError("identifiant du serveur")
            try:
                # on récupère la liste des ids de serveurs
                serveurs = [ i['id'] for i in request.getSession().groupe_serveur ]
                # on récupère le paramètre à modifier
                variable = timeout = variante = None
                erreurs = []
                try:
                    # variable eole
                    variable = escape(request.args[b'variable'][0])
                    valeur = escape(request.args[b'valeur'][0])
                    # on applique la nouvelle valeur
                    erreurs=backend(proxy(request).serveurs.set_groupe_var(serveurs,variable,valeur,True))
                except:
                    # timeout des serveurs
                    try:
                        timeout = escape(request.args[b'timeout'][0])
                        modifs={'timeout':str(int(timeout)*60)}
                        erreurs=backend(proxy(request).serveurs.groupe_params(serveurs,modifs))
                    except:
                        # variante
                        try:
                            variante = escape(request.args[b'variante'][0])
                            modifs={'variante':variante}
                            erreurs=backend(proxy(request).serveurs.groupe_params(serveurs,modifs))
                        except:
                            try:
                                no_alert = escape(request.args[b'no_alert'][0])
                                modifs={'no_alert':no_alert}
                                erreurs=backend(proxy(request).serveurs.groupe_params(serveurs,modifs))
                            except:
                                raise FrontendError("paramètre à modifier")


            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("Erreur lors de l'application des paramètres<br>")


            if type(erreurs) == list and erreurs != []:
                self.content = """<p>paramètres non appliqués sur les serveurs suivants : <br>%s</p>""" % "<br>".join(erreurs)
            else:
                self.content = """<p>Paramètres appliqués.<br/></p>"""
            self.content += """<p><a href="/serveur/action?"""
            if id_groupe is not None:
                # id groupe si groupe enregistré
                self.content += """id_groupe=%s""" % id_groupe
            else:
                # id_serveur = -1 si groupe en mémoire
                self.content += """id=-1"""
            self.content += '">retour à la page des actions</a></p>'

        except Exception as e:
            self.content = e

        return self.content


class ActionDico(Design):
    """Envoie la configuration zephir (sur zephir :)
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'send_dico':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Envoi de configuration"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                dico = request.args[b'dico'][0]
                assert dico
            except Exception:
                raise FrontendError("dictionnaire")
            try:
                serv_module = int(backend(proxy(request).serveurs.get_serveur(id_serveur))[0]['module_actuel'])
                module_version = int(backend(proxy(request).modules.get_module(serv_module))[0]['version'])
                if module_version >= 6:
                    try:
                        # tentative de décodage au format json
                        dico = json.load(dico.strip())
                    except json.decoder.DecodeError:
                        raise BackendError("""Fichier non valide (format JSON attendu)""")
                    # envoi des valeurs
                    dico = [u(str(dico))]
                elif module_version >= 2:
                    # vérification du format et préparation de l'envoi
                    sio = io.StringIO(dico)
                    c = ConfigParser(dict_type=dict)
                    try:
                        c.read_file(sio)
                    except CfgpError:
                        raise BackendError("""Fichier non valide (format '.ini' attendu)""")
                    sio.close()
                    dico = [u(str(c._sections))]
                else:
                    dico = base64.encodebytes(dico)
                backend(proxy(request).serveurs.save_conf(id_serveur,dico))
                self.content = """<p>Sauvegarde du fichier effectuée<br/>
                <a href="aff?id=%s">Retour à la page du serveur</a><br/>
                </p>""" % id_serveur
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")

        except Exception as err:
            self.content = str(err).replace('\n','<br/>')

        return self.content


class ActionConf(Design):
    """prépare l'envoi de configuration d'un serveur (mise en attente uucp)
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'conf':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Configuration de serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                try:
                    check = escape(request.args[b'check'][0])
                except:
                    check = ""
            except Exception:
                raise FrontendError("id_serveur")
            try:
                content = int(escape(request.args[b'content'][0]))
            except:
                raise FrontendError("données à envoyer")
            try:
                if check == 'on':
                    reconf = 1
                else:
                    reconf = 0
                old_clients_msg = ""
                if id_serveur == str(-1):
                    old_clients = backend(proxy(request).uucp.configure_groupe(u(request.getSession().groupe_serveur),reconf, content))
                    if old_clients != []:
                        old_clients_msg = """<br/><font color="red">Un envoi complet a été programmé sur les serveurs suivants (client trop ancien)\
                                </font><br/><ul><li>%s</ul><br/>""" % "<li>".join([str(client) for client in old_clients])
                else:
                    old_clients = backend(proxy(request).uucp.configure(id_serveur, reconf, content))
                    if old_clients != []:
                        old_clients_msg = """<br/><font color="red"Le client de ce serveur n'est pas assez récent, sauvegarde complète programmée<br/>"""

                self.content = """<p>les données seront envoyées à la prochaine connexion des serveurs<br/>%s
                <a href="javascript:history.back()">Retour à la page des actions</a><br/>
                </p>""" % old_clients_msg
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError("Attention, les données n'ont peut-être pas été transferées <br>(données manquantes coté Zéphir ?)")

        except Exception as e:
            self.content = e

        return self.content

class ActionScript(Design):
    """Prépare l'exécution d'un script par le client zephir
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'exec_script':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Éxecution d'un script par le client Zéphir"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            # recherche des paramètres
            try:
                id_serveur = int(escape(request.args[b'id'][0]))
                assert id_serveur
            except Exception:
                raise FrontendError("id_serveur")
            try:
                script_name = escape(request.args[b'script_name'][0])
                assert script_name != ''
            except:
                raise FrontendError("nom du script")
            try:
                params = escape(request.args[b'params'][0])
            except:
                params = ""
            # préparation à l'exécution
            try:
                if id_serveur == -1:
                    serveurs = [serv['id'] for serv in request.getSession().groupe_serveur]
                else:
                    serveurs = [id_serveur]
                backend(proxy(request).uucp.exec_script(serveurs, script_name, params))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError("Erreur lors de la mise en place de l'action : %s<br>" % str(e))

            self.content = """<p>Le script sera exécuté à la prochaine connexion<br/>
            <a href="javascript:history.back()">Retour à la page des actions</a><br/>
            </p>"""

        except Exception as e:
            self.content = e

        return self.content

class ActionSave(Design):
    """sauvegarde la configuration du serveur (sur zephir)
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'save':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Sauvegarde de la configuration du serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("id_serveur")
            try:
                mode = int(escape(request.args[b'mode'][0]))
            except:
                raise FrontendError("données à récupérer")
            try:
                old_clients_msg = ""
                if id_serveur == str(-1):
                    old_clients = backend(proxy(request).uucp.save_conf_groupe(u(request.getSession().groupe_serveur), mode))
                    if old_clients != []:
                        old_clients_msg = """<br/><font color="red">Une sauvegarde complète a été programmée sur les serveurs suivants (client trop ancien)\
                                </font><br/><ul><li>%s</ul><br/>""" % "<li>".join([str(client) for client in old_clients])
                else:
                    old_clients = backend(proxy(request).uucp.save_conf(id_serveur, mode))
                    if old_clients != []:
                        old_clients_msg = """<br/><font color="red"Le client de ce serveur n'est pas assez récent, sauvegarde complète programmée<br/>"""
                self.content = """<p>les données seront récupérées à la prochaine connexion du serveur<br/>%s
                <a href="javascript:history.back()">Retour à la page des actions</a><br/>
                </p>""" % old_clients_msg

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError("Attention, la demande de sauvegarde n'a peut-être pas été prise en compte")

        except Exception as e:
            self.content = e

        return self.content

class ActionReboot(Design):
    """redémarre le serveur
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'reboot':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Redémarrage du serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("id_serveur")
            try:
                delay = int(escape(request.args[b'delay'][0]))
                delay_msg = "<br>Le redémarrage sera différé de %s heures" % str(delay)
            except (ValueError, KeyError):
                delay = 0
                delay_msg = ""
            try:
                if id_serveur == str(-1):
                    # autorisation du reboot sur un groupe de serveur (#6729)
                    old_clients = backend(proxy(request).uucp.reboot_groupe(u(request.getSession().groupe_serveur), delay))
                    self.content = """<p><span id="message">Les serveurs seront redémarrés à leur prochaine connexion</span></p>"""
                else:
                    backend(proxy(request).uucp.reboot(id_serveur, delay))
                    old_clients = []
                    self.content = """<p><span id="message">La demande de redémarrage sera prise en compte
à la prochaine connexion du serveur%s</span></p>""" % delay_msg
                if delay and old_clients:
                    self.content += """<p><font color="red">les serveurs suivants ne seront pas redémarrés
<br>(client trop ancien pour gérer le délai d'exécution) :</font><br>%s</p>""" % ", ".join([str(cli) for cli in old_clients])
                self.content += """<p><a href="javascript:history.back()">Retour à la page des actions</a></p>"""
            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except Exception:
                raise BackendError("""<p><span id="alerte">Attention, la demande de redémarrage n'a pas été prise en compte</span></p>""")

        except Exception as e:
            self.content = e

        return self.content

class ActionReconfigure(Design):
    """reconfigure un serveur
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'reconfigure':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Reconfiguration du serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("id_serveur")
            try:
                delay = int(escape(request.args[b'delay'][0]))
                delay_msg = "<br>L'exécution de reconfigure sera différé de %s heures" % str(delay)
            except (ValueError, KeyError):
                delay = 0
                delay_msg = ""
            try:
                if id_serveur == str(-1):
                    # autorisation du reboot sur un groupe de serveur (#6729)
                    old_clients = backend(proxy(request).uucp.reconfigure_groupe(u(request.getSession().groupe_serveur), delay))
                    self.content = """<p><span id="message">Les serveurs seront reconfigurés à leur prochaine connexion</span></p>"""
                else:
                    backend(proxy(request).uucp.reconfigure(id_serveur, delay))
                    self.content = """<p><span id="message">La demande de reconfiguration sera
prise en compte à la prochaine connexion du serveur%s</span></p>""" % delay_msg
                    old_clients = []
                if delay and old_clients:
                    self.content += """<p><span id="alerte">Les serveurs suivants ne seront pas reconfigurés
<br>(client trop ancien pour gérer le délai d'exécution) :<br>%s</span></p>""" % ", ".join([str(cli) for cli in old_clients])
                self.content += """<p><a href="javascript:history.back()">Retour à la page des actions</a></p>
                </p>"""
            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except Exception:
                raise BackendError("""<p><span id="alerte">Attention, la demande de reconfiguration n'a pas été prise en compte</span></p>""")
        except Exception as e:
            self.content = e
        return self.content

class ActionServiceRestart(Design):
    """Redémarre un service sur un(des) serveur(s)
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'service_restart':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Redémarrage d'un service du serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                service = escape(request.args[b'service'][0])
                assert id_serveur
                assert service
            except Exception:
                raise FrontendError()
            try:
                delay = int(escape(request.args[b'delay'][0]))
                delay_msg = "<br>La relance sera différée de %s heures" % str(delay)
            except (ValueError, KeyError):
                delay = 0
                delay_msg = ""
            try:
                if id_serveur == str(-1):
                    old_clients = backend(proxy(request).uucp.service_restart_groupe(u(request.getSession().groupe_serveur),u(service),delay))
                else:
                    backend(proxy(request).uucp.service_restart(id_serveur,u(service),delay))
                    old_clients = []
                self.content = ""
                self.content = """<p>La demande de relance du service %s sera prise en compte
à la prochaine connexion du serveur%s<p/>""" % (service, delay_msg)
                if delay and old_clients:
                    self.content += """<p><font color="red">Le service ne sera pas redémarré sur les serveurs suivants
<br>(client trop ancien pour gérer le délai d'exécution) :</font><br>%s</p>""" % ", ".join([str(cli) for cli in old_clients])
                self.content += """<p><a href="javascript:history.back()">Retour à la page des actions</a><br/></p>"""

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError("Attention, la demande de redémarrage du service n'a pas été prise en compte")

        except Exception as e:
            self.content = e

        return self.content

class ActionUnlock(Design):
    """Demande la suppression des verrous sur une(des) serveur(s)
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'unlock':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Demande de suppression des verrous"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError()
            try:
                if id_serveur == str(-1):
                    backend(proxy(request).uucp.release_lock_groupe(u(request.getSession().groupe_serveur)))
                else:
                    backend(proxy(request).uucp.release_lock(id_serveur))
                self.content = """<p>les verrous seront supprimés à la prochaine connexion<br/>
                <a href="javascript:history.back()">Retour à la page des actions</a><br/>
                </p>"""

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError("Attention, la demande de suppression des verrous n'a pas été prise en compte")

        except Exception as e:
            self.content = e

        return self.content

class ActionMaj(Design):
    """Prépare une mise à jour automatique sur un serveur
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'maj':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Mise à jour de serveur"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                try:
                    check = escape(request.args[b'check'][0])
                except:
                    check = ""
                try:
                    maj_complete = escape(request.args[b'check_complete'][0])
                except:
                    maj_complete = ""
                try:
                    delay = int(escape(request.args[b'delay'][0]))
                except (ValueError, KeyError):
                    delay = 0
            except Exception:
                raise FrontendError("id_serveur")
            lock_msg = None
            locked = []
            if check == 'on':
                reconf = 1
            else:
                reconf = 0
            if maj_complete == 'on':
                options = "E"
            else:
                options = ""
            try:
                if id_serveur == str(-1):
                    # on vérifie les locks pour les serveurs
                    gr_serv = request.getSession().groupe_serveur
                    for serv in gr_serv:
                        if 'MAJ' in [lock[0] for lock in backend(proxy(request).serveurs.get_locks(serv['id']))]:
                            locked.append(str(serv['id']))
                    if locked != []:
                        lock_msg = """La mise à jour sera ignorée sur les serveurs suivants (fonction MAJ interdite) : """ + ",".join(locked)
                    backend(proxy(request).uucp.maj_groupe(u(request.getSession().groupe_serveur),reconf,delay,options))
                else:
                    if 'MAJ' in [lock[0] for lock in backend(proxy(request).serveurs.get_locks(id_serveur))]:
                        lock_msg = 'la mise à jour est interdite sur ce serveur'
                    else:
                        backend(proxy(request).uucp.maj(id_serveur,reconf,delay,options))

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("Erreur lors de la programmation de la mise à jour<br>")

            if lock_msg is not None:
                self.content = "<p>" + lock_msg + "</p>"
            else:
                self.content = """<p>la mise à jour sera programmée à la prochaine connexion du serveur<br/>"""
            self.content += """<a href="javascript:history.back()">Retour à la page des actions</a><br/>
            </p>"""

        except Exception as e:
            self.content = e

        return self.content

class ActionUpgrade(Design):
    """Prépare le téléchargement des paquets avan migration
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'download_upgrade':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Téléchargement des paquets de migration"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                try:
                    version = int(escape(request.args[b'version'][0]))
                except (ValueError, KeyError):
                    raise FrontendError("version")
                try:
                    delay = int(escape(request.args[b'delay'][0]))
                except (ValueError, KeyError):
                    delay = 0
            except Exception:
                raise FrontendError("id_serveur")
            err_msg = ""
            try:
                if id_serveur == str(-1):
                    erreurs = backend(proxy(request).serveurs.download_upgrade_groupe(u(request.getSession().groupe_serveur), version, delay))
                    # affichage des erreurs
                    if erreurs != []:
                        err_msg += """<p>L'action a échoué pour les serveurs suivants :<br/>"""
                        for err in erreurs:
                            err_msg += "%s<br/>" % err
                        err_msg += "</p>"
                else:
                    backend(proxy(request).serveurs.download_upgrade(id_serveur, version, delay))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as err:
                raise BackendError(err)
            else:
                self.content = """<p>le téléchargement sera programmé à la prochaine connexion du serveur<br/>"""

            self.content += err_msg
            self.content += """<a href="javascript:history.back()">Retour à la page des actions</a><br/>
            </p>"""

        except Exception as e:
            self.content = e

        return self.content

class ActionMajClient(Design):
    """Prépare une mise à jour du client zephir
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'maj_client':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Envoi de zephir-client"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("id_serveur")
            try:
                if id_serveur == str(-1):
                    backend(proxy(request).uucp.maj_client_groupe(u(request.getSession().groupe_serveur)))
                else:
                    backend(proxy(request).uucp.maj_client(id_serveur))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("Attention, la mise à jour ne sera certainement pas effectuée<br>")

            self.content = """<p>Le client se mettra à jour à la prochaine connexion<br/>
            <a href="javascript:history.back()">Retour à la page des actions</a><br/>
            </p>"""

        except Exception as e:
            self.content = e

        return self.content

class ActionRegenCert(Design):
    """prépare la regénération des clés d'enregistrement ssh et des certificats ssl
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'regen_cert':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Regénération des clés"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
                try:
                    check = escape(request.args[b'check_ssl'][0])
                except:
                    check = ""
            except Exception:
                raise FrontendError("id_serveur")
            try:
                if check == 'on':
                    regen_ssl = True
                else:
                    regen_ssl = False
                if id_serveur == "-1":
                    serveurs = [serv['id'] for serv in request.getSession().groupe_serveur]
                else:
                    serveurs = [id_serveur]
                erreurs = backend(proxy(request).serveurs.regen_key(serveurs,regen_ssl))
                self.content = """\n<p>les clés seront mises en place à la prochaine connexion des serveurs<br/>"""
                if erreurs != []:
                    self.content += """\n<br/>Erreur lors de la préparation des clés sur les serveurs suivants :<br/>"""
                    for err in erreurs:
                        self.content += """\n%s<br/>"""  % err
                    self.content += """</br>"""
                self.content += """<a href="javascript:history.back()">Retour à la page des actions</a><br/>
                </p>"""
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError("Erreur lors de la préparation des clés : %s" % str(e))

        except Exception as e:
            self.content = e

        return self.content

class EtatServeur(Design):
    """Affichage de l'etat d'un serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"etat" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "État général du serveur"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation(aide="howto/page5.html").menu()

    def _dump_html(self,status,id_serveur,id_groupe,info_serv,etat_global,detail_agents,request,creole_version):
        """Affichage de la liste des actions loguées
        """
        # dictionnaire d'info des modules
        modules = backend(proxy(request).modules.get_module())
        mod_infos = {}
        for mod in modules:
            mod_infos[mod['id']] = mod
        msg = ['<h1>État actuel du serveur %s </h1><h2>Établissement <a href=/etab/get?rne=%s>%s</a> - version %s - identifiant %s</h2><table id="tab_etat_placement" cellpadding="1" cellspacing="2" width="90%%">' % (info_serv['libelle'],info_serv['rne'],info_serv['rne'],mod_infos[info_serv['module_actuel']]['libelle'],id_serveur)]

        msg.append("""<tr><td class="tab_etat_libelle">Configuration </td>""")
        msg.append("""<td><table class="tab_etat">""")
        if creole_version == "creole3":
            edit_url = "javascript:void(window.open('/genconfig1/serveur/%s/%%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_serveur
        else:
            edit_url = "javascript:void(window.open('/genconfig/serveur/%s/%%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_serveur
        # affichage de la présence ou non des fichiers de configuration
        fics = [['dico_ok','configuration de variante'],['config_ok','configuration du serveur'],['cle_ok','enregistrement Zéphir']]
        for fic in fics:
            if status[fic[0]] == 0:
                if fic[0] == 'dico_ok':
                    msg.append("""<tr><td><img border="0" src="/images/diode_grise.gif" title="le fichier n'est pas présent sur le serveur Zéphir"></td>""")
                else:
                    msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="le fichier n'est pas présent sur le serveur Zéphir"></td>""")
            else:
                if fic[0] == "config_ok" and status['dictpaqs_ok'][0] == 0:
                    msg.append("""<tr><td><img border="0" src="/images/diode_orange.gif" title="dictionnaires non pris en compte : des modifications\n faites sur ce serveur risquent d'être écrasées"></td>""")
                else:
                    msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif" title="fichier présent sur le serveur Zéphir"></td>""")
            if fic[0] == 'cle_ok':
                new_key_label = ""
                if status['new_key'][0] == 1:
                    new_key_label = "&nbsp;(nouvelle clé en attente de mise en place)"
                elif status['new_key'][0] == 2:
                    new_key_label = "&nbsp;(IP Zéphir à envoyer au client : %s)" % status['new_key'][1]
                elif status['new_key'][0] == 3:
                    new_key_label = "&nbsp;(IP Zéphir prise en compte par le client : %s)" % status['new_key'][1]
                msg.append("""<td>%s%s</td></tr>""" % (fic[1],new_key_label))
            else:
                if fic[0] == 'dico_ok':
                    # traitement de dico.eol
                    mode = 'dico'
                    if status[fic[0]] == 1:
                        dl_var_link = """[<a href="/serveur/get_dico?id=%s&mode=modif_dico">télécharger</a>]&nbsp;""" % (id_serveur)
                    else:
                        dl_var_link = ""
                    msg.append("""<td>%s&nbsp;&nbsp;%s(modifiable dans la variante)</td></tr>""" % (fic[1], dl_var_link))
                else:
                    # traitement de zephir.eol
                    mode = 'config'
                    # lien vers le formulaire de saisie
                    if status[fic[0]] == 1:
                        # si fichier présent, on fait le lien sur sa modification et sa regénération + lien de téléchargement
                        msg.append("""<td>%s&nbsp;&nbsp;[<a href="%s">modifier</a>/<a href="%s">générer</a>/<a href="/serveur/get_dico?id=%s&mode=modif_config">télécharger</a>]</td></tr>""" % (fic[1], edit_url % ('modif_'+mode,), edit_url % mode, id_serveur))
                    else:
                        # sinon on fait le lien seulement sur sa regénération
                        msg.append("""<td>%s&nbsp;&nbsp;[<a href="%s">générer</a>]</td></tr>""" % (fic[1],edit_url % mode))

        # gestion des fichiers de migration
        if status['migration_ok'] == -1 and mod_infos[info_serv['module_actuel']]['version'] in config.allowed_migrations:
            # pas de fichier de migration, on propose de le créer
            msg.append("""<tr><td><img border="0" src="/images/diode_grise.gif" title="pas de données de migration enregistré"></td>""")
            msg.append("""<td>migration&nbsp;&nbsp;[<a href="/serveur/migrate_conf?id=%s">générer les données de migration</a>]</td></tr>""" % (id_serveur))
        elif status['migration_ok'] == 0 :
            # fichier de migration renseigné
            try:
                variante_dest = backend(proxy(request).serveurs.variante_migration(id_serveur))
                var_info = backend(proxy(request).modules.get_variante(variante_dest))[0]
                mod_dest = backend(proxy(request).modules.get_module(var_info['module']))[0]
                # on vérifie si on fait une migration vers eole 2.4 (lancement de genconfig pour modifier)
                creole_dest = config.CREOLE_VERSIONS[mod_dest['version']]
                if creole_dest == "creole3":
                    modif_url  = "javascript:void(window.open('/genconfig1/migrate/%s/%s/%%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" \
                    % (id_serveur, variante_dest)
                else:
                    modif_url  = "javascript:void(window.open('/genconfig/migrate/%s/%s/%%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" \
                    % (id_serveur, variante_dest)
                msg.append("""<tr><td><img border="0" src="/images/diode_orange.gif" title="données de migration enregistrées"></td>""")
                msg.append("""<td>migration&nbsp;&nbsp;[<a href="%s">modifier</a>/<a href="/serveur/migrate_conf?id=%s">générer</a>/<a href="/serveur/get_dico?id=%s&mode=modif_migration">télécharger</a>]</td></tr>""" % (modif_url % 'modif_migration', id_serveur, id_serveur))
                msg.append("""<tr><td></td><td> variante de migration : {0} - {1} ({2})</td></tr>""".format(mod_dest['libelle'], var_info['libelle'], var_info['id']))
            except:
                # erreur de lecture des infos de migration déjà renseignées, on permet une nouvelle saisie
                log.msg("serveur %s: Erreur de récupération des informations de la variante de migration" % (str(id_serveur),))
                msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="Erreur de lecture des données de migration (variante supprimée ?)"></td>""")
                msg.append("""<td>migration&nbsp;&nbsp;[<a href="/serveur/migrate_conf?id=%s">générer les données de migration</a>]</td></tr>""" % (id_serveur))
        if info_serv['md5s'] == 0:
            # zephir.eol ou des patchs sont modifiés sur le serveur
            nb_modifs = len(status['md5s'][1].split(';'))
            if nb_modifs > 1:
                pluriel = 's'
            else:
                pluriel = ''
            msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="modification de configuration détectée"></td><td><select><option>détection de %s fichier%s modifié%s</option><option>""" % (str(nb_modifs), pluriel, pluriel)  + """</option><option>""".join(status['md5s'][1].split(';')) + """</option></select></td></tr>""")
        elif info_serv['md5s'] == -1:
            msg.append("""<tr><td><img border="0" src="/images/diode_grise.gif"></td><td>fichier de sommes de contrôle non disponible</td></tr>""")
        else:
            msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif"></td><td>pas de modifications détectées</td></tr>""")
        # Détection de paquets avec dictionnaires installés manuellement
        # si le module utilise le pool de dictionnaires
        if status['dictpaqs_ok'][0] == 1:
            msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif"></td><td>paquets synchronisés</td></tr>""")
        elif status['dictpaqs_ok'][0] == 0:
            liste_paqs = status['dictpaqs_ok'][1]
            options = ''
            total = 0
            if liste_paqs:
                total += len(liste_paqs)
                if len(liste_paqs) == 1:
                    label = "1 paquet installé manuellement"
                else:
                    label = "%d paquets installés manuellement" % len(liste_paqs)
                options += """<optgroup label="%s"><option>%s</option></optgroup>""" % (label, "</option><option>".join(liste_paqs))
            extra_paqs = status['dictpaqs_ok'][2]
            if extra_paqs:
                total += len(extra_paqs)
                if len(extra_paqs) == 1:
                    label = "1 paquet non installé"
                else:
                    label = "%d paquets non installés" % len(extra_paqs)
                options += """<optgroup label="%s"><option>%s</option></optgroup>""" % (label, "</option><option>".join(extra_paqs))
            if total > 1:
                pluriel = 's'
            else:
                pluriel = ''
            select_paqs = """<select><option>détection de %s paquet%s en erreur</option>%s</select>""" % (str(total), pluriel, options)
            msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="paquets en erreur détectés"></td><td>%s</td></tr>""" % select_paqs)
        else:
            msg.append("""<tr><td><img border="0" src="/images/diode_grise.gif"></td><td>paquets installés non disponible</td></tr>""")
        #lien sur la page de consultation des données personnalisées
        conf_vpn=""
        if mod_infos[info_serv['module_actuel']]['libelle'].startswith('sphynx-'):
            conf_vpn="""&nbsp;-&nbsp;<a href="sphynx_vpn?id=%s">configurations RVP</a>""" % id_serveur
        conf_repl=""
        if backend(proxy(request).uucp.check_replication(int(id_serveur))) != 0:
            conf_repl="""&nbsp;-&nbsp;<a href="ldap_repl?id=%s">configurations de réplication LDAP</a>""" % id_serveur
        msg.append("""</table></td></tr><tr><td class="tab_etat_libelle" colspan="2" align=center><a href="perso?id=%s">voir les fichiers personnalisés</a>%s%s</td></tr>""" % (id_serveur, conf_vpn, conf_repl))

        # affichage des informations sur uucp (file d'attente)
        msg.append("<tr><td class=\"tab_etat_libelle\">File d'attente des échanges</td>")
        uucp_stopped = ""
        if etat_global in [3,4]:
            uucp_stopped = "<font color=red>!file d'attente bloquée (problème UUCP ou SSH)!</font><BR/>"
        msg.append("""<td class="tab_etat_libelle">%s transferts : %s<BR> commandes : %s &nbsp;<a href="/serveur/uucp?id=%s">liste des commandes en attente</a></td></tr>""" % (uucp_stopped,status['uucp_transfert'],status['uucp_cmd'],str(id_serveur)))
        msg.append("""<tr><td class="tab_etat_libelle">État actuel des actions""")
        # légende
        # msg.append("""<br><img border="0" src="/images/diode_verte2.gif">pas d'erreurs""")
        # msg.append("""<br><img border="0" src="/images/diode_orange.gif">action en cours""")
        # msg.append("""<br><img border="0" src="/images/diode2.gif">échec (laisser la souris au dessus<br>du bouton pour plus de détails)""")
        msg.append("""</td><td><table class="tab_etat"> """)

        # erreurs reportées dans les logs
        actions={'maj_ok':'mise &agrave; jour',
                'reconfigure_ok':'reconfiguration du serveur',
                'configure_ok':'mise en place de la configuration',
                'sauvegarde_ok':'sauvegarde de la configuration',
                'service_restart_ok':'red&eacute;marrage de service',
                'reboot_ok':'red&eacute;marrage &agrave distance du serveur',
                'upgrade_ok':'pr&eacute;chargement des paquets (Upgrade-Auto)',
                'perso_ok':'ex&eacute;cution de scripts personnalis&eacute;s',
                }

        for action in list(actions.keys()):
            if action == 'maj_ok':
                # si on a des infos sur les paquets non à jour, on les affiche ici
                if 'query_maj' in status:
                    if int(status['query_maj'][0]) > 0:
                        # liste des paquets disponibles
                        try:
                            info_paq = backend(proxy(request).serveurs.get_maj_infos(int(id_serveur)))
                            if len(info_paq) > 0:
                                liste_paq = """<select><option>""" + "</option><option>".join([paq[0] for paq in info_paq]) + """</option></select>"""
                            else:
                                liste_paq = ""
                        except:
                            liste_paq = ""
                        data_maj = """</td></tr><td></td><td><font color="red">%s paquet(s) non à jour</font> %s""" % (str(status['query_maj'][0]), liste_paq)
                    elif int(status['query_maj'][0]) == -1:
                        data_maj = "(erreur Query-Auto : %s)" % str(status['query_maj'][1])
                    else:
                        data_maj = ""

                    if mod_infos[info_serv['module_actuel']]['version'] >= 2:
                        # lien sur la liste des paquets installés (eole NG seulement)
                        actions[action] += """&nbsp;&nbsp;[<a href=/serveur/info_maj?id=%s>afficher le détail des paquets installés</a>]%s""" % (id_serveur,data_maj)
            if status[action][0] == 0:
                msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="%s"></td><td> %s </td></tr>""" % (str(status[action][1])+' : '+status[action][2],actions[action]))
            elif status[action][0] == 2:
                msg.append("""<tr><td><img border="0" src="/images/diode_orange.gif" title="%s"></td><td> %s </td></tr>""" % (str(status[action][1])+' : '+status[action][2],actions[action]))
            elif status[action][0] == -2:
                msg.append("""<tr><td><img border="0" src="/images/diode_grise.gif" title="%s"></td><td> %s </td></tr>""" % ("Aucune information disponible",actions[action]))
            else:
                msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif" title="%s"></td><td> %s </td></tr>""" % (str(status[action][1])+' : '+status[action][2],actions[action]))

        # gestion lock d'uucp
        if status['lock_ok'][0] != 1:
            msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="%s"></td><td> verrouillage des fonctions zephir </td></tr>""" % (str(status['lock_ok'][1])+": une action zephir s'est mal terminée sur le serveur") )
        else:
            msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif" title="zephir est actif sur ce serveur"></td><td> verrouillage des fonctions zephir </td></tr>""")
        if 'del_locks' in status:
            if status['del_locks'] == True:
                msg.append("""<tr><td colspan="2">&nbsp;&nbsp;<font color="green">** suppression des verrous demandée **</font></td></tr>""")
        # gestion timeout
        ip_pub = ""
        if info_serv['ip_publique']:
            ip_pub = """&nbsp;(adresse IP publique : %s)""" % info_serv['ip_publique']
        if status['timeout'][0] != 1:
            msg.append("""<tr><td><img border="0" src="/images/diode2.gif" title="date du dernier contact : %s"></td><td> contact avec le serveur%s</td></tr>""" % (status['timeout'][1], ip_pub))
        else:
            msg.append("""<tr><td><img border="0" src="/images/diode_verte2.gif" title="date du dernier contact : %s"></td><td> contact avec le serveur%s</td></tr>""" % (status['timeout'][1], ip_pub))
        # etat des services
        if status['agents'] == 1:
            msg.append("""</table></td></tr><tr><td class="tab_etat_libelle">État des services</td><td><table class="tab_etat"><tr><td><img border="0" src="/images/diode_verte2.gif" title="état des services remontés par le serveur"> pas de problème signalé</td></tr></table>""")
        elif status['agents'] == 0:
            # au moins un des services a remonté une alerte, on regarde lesquels sont en erreur
            title = ''
            if detail_agents["errors"]:
                title += "agents en erreur : %s" % ", ".join(detail_agents["errors"])
            if detail_agents["warnings"]:
                if title:
                    title += "\n"
                title += "agents en avertissement : %s" % ", ".join(detail_agents["warnings"])
            msg.append("""</table></td></tr><tr><td class="tab_etat_libelle">État des services</td><td><table class="tab_etat"><tr><td><img border="0" src="/images/diode2.gif" title="%s"> détail dans la page de surveillance</td></tr></table>""" % title)
        elif status['agents'] == 2:
            # au moins un des services a remonté une alerte, on regarde lesquels sont en warnings
            title = ''
            if detail_agents["errors"]:
                title += "agents en erreur : %s" % ", ".join(detail_agents["errors"])
            if detail_agents["warnings"]:
                if title:
                    title += "\n"
                title += "agents en avertissement : %s" % ", ".join(detail_agents["warnings"])
            msg.append("""</table></td></tr><tr><td class="tab_etat_libelle">État des services</td><td><table class="tab_etat"><tr><td><img border="0" src="/images/diode_orange.gif" title="%s"> détail dans la page de surveillance</td></tr></table>""" % title)
        else:
            msg.append("""</table></td></tr><tr><td>État des services</td><td><table border="0"><tr><td><img border="0" src="/images/diode_grise.gif" title="Pas de statistique présente pour ce serveur"> information non disponible</td></tr></table>""")
        # lien sur la page des logs
        msg.append("""</td></tr><tr><td class="tab_etat_libelle" colspan="2" align="center"><a href="log?id=%s&mode=zlog&check_action=1">afficher les logs complets</a> (date du dernier log : %s )</td></tr>""" % (id_serveur,status['last_log']))
        msg.append("""</table>""")
        msg.append("""<p><a href="aff?id=%s">Édition du serveur</a>""" % id_serveur)
        msg.append("""&nbsp;/&nbsp;<a href="/serveur/action?id=%s">Actions sur le serveur</a>&nbsp;/&nbsp;<a href="javascript:void(window.open('https://'+window.location.hostname+':%s/agents/%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))">Surveillance du serveur</a></p>""" % (id_serveur,config.PORT_HTTP,id_serveur))
        # liste des groupes existants
        existing_gr = backend(proxy(request).serveurs.get_groups())
        existing_gr.sort(key=lambda x:x[1].lower())
        if id_groupe:
            msg.append("""<p><a href="aff_groupe?id_groupe=%s">Retour à la page du groupe</a></p>""" % str(id_groupe))
        if existing_gr != []:
            gr_ok = []
            for gr in existing_gr:
                if info_serv['id'] not in gr[2]:
                    gr_ok.append(gr)
            if gr_ok != []:
                msg.append("""<script language='javascript'>
                function check_group() {
                liste = document.forms['group_add']['extend_gr'];
                but_gr = document.forms['group_add']['gr_sub'];
                if (liste.options[liste.selectedIndex].value != "")
                {
                    but_gr.disabled = "";
                } else {
                    but_gr.disabled = "disabled";
                }
                }
                </script>""")
                msg.append("""<p><form METHOD="POST" name="group_add" action="/serveur/extend_groupe" onChange="javascript:check_group(this)">""")
                msg.append("""<input type="hidden" name="id_serveur" value="%d"/>""" % info_serv['id'])
                msg.append("""<select name=extend_gr>""")
                msg.append("""    <OPTION VALUE="" selected="selected"></OPTION>""")
                for gr in gr_ok:
                    if info_serv['id'] not in gr[2]:
                        msg.append("""    <OPTION VALUE="%s">%s</OPTION>""" % (gr[0],gr[1]))
                msg.append("""</select><input type="submit" name="gr_sub" disabled="disabled" value="Ajouter à ce groupe"></form></p><br/>""")
        return "\n".join(msg)

    def renderView(self, request):
        try:
            id_groupe = None
            info_serv = []
            detail_agents = {'errors': [], 'warnings': []}
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                pass
            try:
                info_serv = backend(proxy(request).serveurs.get_serveur(int(id_serveur)))
                if info_serv == []:
                    self.content = """<CENTER><H1> Serveur non trouvé </H1><p><a href="javascript:history.back()">Retour à la page précédente</a></p></CENTER>"""
                else:
                    # informations de status du serveur
                    liste = backend(proxy(request).serveurs.get_status(int(id_serveur)))
                    if liste['agents'] in [0, 2]:
                        try:
                            data_agents = backend(proxy(request).serveurs.agents_status(int(id_serveur)))
                            for cle, data in list(data_agents.items()):
                                if data[1] == 0:
                                    detail_agents['errors'].append(data[0])
                                elif data[1] == 5:
                                    detail_agents['warnings'].append(data[0])
                        except:
                            detail_agents["errors"].append('erreur de récupération du détail des agents')
                    try:
                        etat_global = backend(proxy(request).serveurs.global_status(int(id_serveur)))
                    except:
                        etat_global = -1
                    # version creole du serveur
                    creole_version = backend(proxy(request).serveurs.creole_version(int(id_serveur)))
                    self.content = self._dump_html(liste,id_serveur,id_groupe,info_serv[0],etat_global,detail_agents,request,creole_version)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                traceback.print_exc()
                raise BackendError

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class LogServeur(Design):
    """Affichage des logs du backend
    """
    isLeaf = True

    def param_inputs(self,type_params):
        res = []
        for key, value in list(type_params.items()):
            if value == 'checked':
                res.append("""<input type="hidden" name="%s" value="on"/>""" % key)
        return "\n".join(res)

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"log" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Logs du serveur"

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self,liste,id_serveur,mode,type_params,pagenb):
        """Affichage de la liste des actions loguées
        """
        s = """<p><a href="etat?id=%s">Retour à la page d'état du serveur</a></p>
        <table id="tab_log" cellpadding="0" cellspacing="2" width="96%%"><tr>""" % id_serveur
        if mode == 'zlog':
            # boutons de navigation dans les pages de log
            nbitems = 50
            if len(liste) > nbitems:
                # numéro de page max
                maxpage = int(round(len(liste) / float(nbitems))) - 1
                # on coupe à 50 lignes par page
                start = pagenb * nbitems
                end = start + nbitems
                liste = liste[start:end]
                # première page
                s += """<td colspan="2" align="left">"""
                s += """<table><tr><td><FORM METHOD=POST ACTION="log?id=%s&mode=%s">
                <input type="hidden" name="page" value="0"/>
                <input type=SUBMIT value="Début"/>%s</FORM></td>""" % (id_serveur,mode,self.param_inputs(type_params))
                if pagenb > 0:
                    # page précédente
                    s += """<td><FORM METHOD=POST ACTION="log?id=%s&mode=%s">
                    <input type="hidden" name="page" value="%s"/>
                    <input type=SUBMIT value="<-"/>%s</FORM></td>""" % (id_serveur,mode,str(pagenb-1),self.param_inputs(type_params))
                if pagenb < maxpage:
                    # page suivante
                    s += """<td><FORM METHOD=POST ACTION="log?id=%s&mode=%s">
                    <input type="hidden" name="page" value="%s"/>
                    <input type=SUBMIT value="->"/>%s</FORM></td>""" % (id_serveur,mode,str(pagenb+1),self.param_inputs(type_params))
                # dernière page
                s += """<td><FORM METHOD=POST ACTION="log?id=%s&mode=%s">
                <input type="hidden" name="page" value="%s"/>
                <input type=SUBMIT value="Fin"/>%s</FORM></td>
                <td>%s/%s</td></tr></table></td>""" % (id_serveur,mode,str(maxpage),self.param_inputs(type_params),pagenb+1,maxpage+1)
            else:
                s+="""<td colspan="2"></td>"""
            s += """<td colspan="2" align="right"><FORM METHOD=POST ACTION="/serveur/log?id=%s&mode=%s">
            <input type=submit value="Appliquer le filtre">
            <input type="checkbox" name="check_action" %s/>actions
            <input type="checkbox" name="check_surv" %s/>surveillance
            <input type="checkbox" name="check_div" %s/>divers</form></td></tr>
            <tr><td class="td_titre">Date</td><td class="td_titre">Action</td><td class="td_titre">État</td><td class="td_titre">Message</td>
            """ % (id_serveur,mode,type_params['check_action'],type_params['check_surv'],type_params['check_div'])
            s += """</td></tr>"""
        else:
            s += """<tr><td>date de la demande</td><td>action demandée</td></tr>"""

        for d in liste:
            if int(d['etat']) == 0:
                d['etat'] = "OK"
            elif int(d['etat']) > 0:
                d['etat'] = "<B>ÉCHEC</B>"
            elif int(d['etat']) == -2:
                d['etat'] = "<B>INFO</B>"
            else:
                d['etat'] = "EN COURS"

            if mode == 'zlog':
                s += """<tr><td>%(date)s</td><td>%(action)s</td><td>%(etat)s</td><td>%(message)s</td></tr>""" % d
            else:
                s += """<tr><td>%(date)s</td><td>%(message)s</td></tr>""" % d

        s += """</table><p><a href="etat?id=%s">Retour à la page d'état du serveur</a></p>""" % id_serveur
        return s

    def renderView(self, request):
        try:
            try:
                liste_types = []
                id_serveur = escape(request.args[b'id'][0])
                try:
                    mode = escape(request.args[b'mode'][0])
                    try:
                        pagenb = int(escape(request.args[b'page'][0]))
                    except:
                        pagenb = 0
                    type_params = {}
                    types={'check_action':['CONFIGURE','RECONFIGURE','MAJ','SAUVEGARDE','INSTANCE','SERVICE_RESTART','UPGRADE','PERSO'],
                           'check_surv':['SURVEILLANCE'],
                           'check_div':['ZEPHIR','LOCK','RVP','QUERY-MAJ']}
                    for type_log in list(types.keys()):
                        if type_log in request.args:
                            type_params[type_log]="checked"
                            liste_types.extend(types[type_log])
                        else:
                            type_params[type_log]=""
                except:
                    mode = 'zlog'
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            try:
                liste = backend(proxy(request).serveurs.get_log(int(id_serveur),u(mode), liste_types))
                if mode == 'zlog':
                    self.content = """<h1>Liste des derniers messages provenant du serveur</h1>%s""" % self._dump_html(liste,id_serveur,mode,type_params,pagenb)
                else:
                    self.content = """<h1>Liste des dernières actions envoyées au serveur</h1>%s""" % self._dump_html(liste,id_serveur,mode,type_params,pagenb)
            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</spam></p>""")
            except Exception as e:
                raise BackendError

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class ConfVpn(Design):
    """Affichage des configurations RVP enregistrées"""
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"sphynx_vpn" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Configurations RVP"

    def wmfactory_menu(self, request):
        return Navigation(aide="page6.html").menu()

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self,liste,id_serveur):
        """Affichage de la liste des configurations RVP importées/en place
        """
        s = """<a href="etat?id=%s">Retour à la page d'état du serveur</a><br><br><table id="tab_id_rvp" cellpadding="1" cellspacing="2">""" % id_serveur

        s += """<tr><td>id amon</td><td>descriptif</td><td>avancement de la configuration</td><td colspan="2"></td></tr>"""

        for conf in liste:
            supression=""
            if int(conf[1]) == 0:
                # fichier importé mais non installé
                img = """archive présente</td><td colspan="2"><img border="0" src="/images/diode_grise.gif"/>"""
            elif int(conf[1]) == 1:
                # fichier récupéré sur amon
                img = """RVP activé</td><td><img border="0" src="/images/diode_orange.gif"/>"""
                supression="""<FORM action="sphynx_del" method="POST" border="0"> \
<input type="hidden" name="id" value="%s"/> \
<input type="hidden" name="amon" value="%s"/> \
<input type="hidden" name="mode" value="0"/> \
<td><input type="submit" value="effacer l'archive"/></td></form>""" % (id_serveur,conf[0])
            elif int(conf[1]) == 2:
                # fichier récupéré et supprimé
                img = """RVP activé (archive supprimée)</td><td colspan="2"><img border="0" src="/images/diode_verte2.gif"/>"""
            elif int(conf[1]) == 3:
                # fichier supprimé mais pas récupéré
                img = """archive supprimée avant utilisation !</td><td colspan="2"><img border="0" src="/images/diode2.gif"/>"""
            s += """<tr><td>%s<a href="sphynx_del?id=%s&amon=%s&mode=1">(supprimer)</a></td><td>%s</td><td>%s</td>%s</tr>""" % (conf[0],id_serveur,conf[0],conf[2],img,supression)
        s += """</table>"""
        return s

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            try:
                liste_conf = backend(proxy(request).uucp.sphynx_list(id_serveur))
                # recherche des libellés de serveurs
                liste = []
                for serveur in liste_conf:
                    data_serv = backend(proxy(request).serveurs.get_serveur(serveur[0]))[0]
                    liste.append([serveur[0],serveur[1],data_serv['libelle']+' - '+data_serv['rne']])
                data_sphynx = backend(proxy(request).serveurs.get_serveur(id_serveur))[0]
                self.content = """<h1>Configurations RVP - %s (%s)</h1>%s""" % (data_sphynx['libelle'],data_sphynx['id'],self._dump_html(liste,id_serveur))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError(e)

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content


class DelVpn(Design):
    """Suppression d'une configuration RVP sur un sphynx
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"sphynx_del":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression de configuration RVP"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id = int(escape(request.args[b'id'][0]))
                id_amon = int(escape(request.args[b'amon'][0]))
                mode = int(escape(request.args[b'mode'][0]))
            except:
                raise FrontendError()
            try:
                backend(proxy(request).uucp.sphynx_del(id,id_amon,mode))
                if mode == 1:
                    msg = "la configuration RVP"
                else:
                    msg = "l'archive"
                self.content = """
                <p>%s a été supprimée<br/></p>
                <p><a href="sphynx_vpn?id=%s">retour à la page précédente</a></p>
                """ % (msg,id)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError()

        except (FrontendError,BackendError) as e:
                self.content = e

        return self.content

class ConfRepl(Design):
    """Affichage des configurations de réplication LDAP"""
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"ldap_repl":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Configurations de réplication LDAP"

    def wmfactory_menu(self, request):
        return Navigation(aide="").menu()

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self, liste, id_serveur, repl_state):
        """Affichage de la liste des configurations de réplication LDAP en place
        """
        if len(liste) > 10:
            s = """<a href="etat?id=%s">Retour à la page d'état du serveur</a><br><br>""" % id_serveur
        else:
            s = ""

        s += """<table id="tab_etat" cellpadding="1" cellspacing="2">"""
        s += """<tr><td class="col" colspan="2">Fichier(s) de configuration des annuaires à répliquer</td></tr>"""

        liste.sort()
        for conf in liste:
            s += """<tr><td>%s</td><td><a href=del_repl?id=%s&nom_fichier=%s">Supprimer ce fichier</a></td>""" % (conf, id_serveur, conf)
        if repl_state == 2:
            s += """<tr><FORM action="ldap_repl" method="POST" border="0"><td colspan="2" align="center">
    <input type="hidden" name="id" value="%s"/>
    <input type="hidden" name="update_conf" value="yes"/>
    <input type="submit" value="Envoyer ces configurations au serveur de réplication"/></td></FORM></tr>""" % id_serveur
        s += """</table>"""
        s += """<br><a href="etat?id=%s">Retour à la page d'état du serveur</a><br><br>""" % id_serveur
        return s

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            self.content = ""
            # suppression de configuration si demandée
            if b'del_fic' in request.args:
                del_fic = escape(request.args[b'del_fic'][0])
                try:
                    backend(proxy(request).uucp.del_replication(id_serveur, del_fic))
                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except:
                    self.content += "<script language='javascript'>alert('Erreur de suppression de %s');</script>" % del_fic
            # mise à jour de la configuration du serveur si demandée
            if b'update_conf' in request.args:
                if escape(request.args[b'update_conf'][0]) == 'yes':
                    try:
                        res = backend(proxy(request).uucp.update_replication(id_serveur))
                        self.content += "<script language='javascript'>alert('La configuration sera mise à jour à la prochaine connexion du serveur');</script>"
                    except xmlrpclib.ProtocolError:
                        raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                    except:
                        self.content += "<script language='javascript'>alert('Erreur lors la préparation d'envoi de la configuration')</script>"
            # affichage des configurations enregistrées
            try:
                liste_conf = backend(proxy(request).uucp.get_replication(id_serveur))
                repl_state = backend(proxy(request).uucp.check_replication(id_serveur))
                data_serv = backend(proxy(request).serveurs.get_serveur(id_serveur))[0]
                # recherche des libellés de serveurs
                self.content += """<h1>Configurations de réplication LDAP - %s (%s)</h1>%s""" % (data_serv['libelle'],data_serv['id'],self._dump_html(liste_conf, id_serveur, repl_state))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError(e)

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content


class DelRepl(Design):
    """Suppression d'une configuration de réplication LDAP
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"del_repl":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression de configuration de réplication LDAP"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id_serv = escape(request.args[b'id'][0])
                nom_fichier = escape(request.args[b'nom_fichier'][0])
            except:
                raise FrontendError()
            try:
                self.content = """<p>Voulez-vous vraiment supprimer le fichier %s ?<br/>
                <a href="ldap_repl?id=%s&del_fic=%s">Suppression</a><br/>
                <a href="ldap_repl?id=%s">Annuler</a></p>
                """ % (nom_fichier, id_serv, nom_fichier, id_serv)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError()

        except (FrontendError,BackendError) as e:
                self.content = e

        return self.content


class Uucp(Design):
    """Affichage des commandes en attente
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"uucp":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Commandes en attente"

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self,pool,id_serveur):
        """Affichage de la liste des actions en attente
        """
        s = """ <table id="tab_commandes" cellpadding="1" cellspacing="2">"""
        s += """<tr><td class="td_titre">numéro d'ordre</td><td class="td_titre">action demandée</td></tr>"""
        cmds = pool[0]
        files = pool[1]
        for cmd in cmds:
            s += """<tr><td>%s<a href="uucp?id=%s&purge=%s">&nbsp;(annuler)</a></td><td>%s</td></tr>""" % (cmd[0],id_serveur,cmd[0],cmd[1])
        s += """</table></br><p><a href="uucp?id=%s&purge=all">Purger la file d'attente</a></p>""" % id_serveur
        return s

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                id_groupe = None
            try:
                id_tache=escape(request.args[b'purge'][0])
            except:
                pass
            else:
                try:
                    if id_tache == "all":
                        liste = backend(proxy(request).uucp.purge_actions([id_serveur]))
                    else:
                        liste = backend(proxy(request).uucp.purge_actions([id_serveur],id_tache))

                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except Exception as e:
                    raise BackendError(e)

            try:
                pool = backend(proxy(request).uucp.get_actions(int(id_serveur)))
                self.content = """<h1>Liste des dernières actions envoyées au serveur</h1>"""
                self.content += self._dump_html(pool,id_serveur)
                if id_groupe is None:
                    self.content += """<p><a href="etat?id=%s">Retour à la page d'état du serveur</a></p>""" % id_serveur
                else:
                    self.content += """<p><a href="etat?id=%s&id_groupe=%s">Retour à la page d'état du serveur</a></p>""" % (id_serveur, id_groupe)
                if id_groupe is not None:
                    self.content += """<p><a href="/serveur/aff_groupe?id_groupe=%s">Retour à la page du groupe</a></p>""" % id_groupe
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except :
                raise BackendError

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class PurgeUucp(Design):
    """Purge les actions uucp en attente
    """
    isLeaf=True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'purge_uucp':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Purge des actions du groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id_serveur'][0])
                assert id_serveur
            except:
                raise FrontendError("identifiant du serveur")
            try:
                if id_serveur == str(-1):
                    # on récupère la liste des ids de serveurs
                    serveurs = [ i['id'] for i in request.getSession().groupe_serveur]
                    backend(proxy(request).uucp.purge_actions(serveurs))
                else:
                    # purge d'un serveur particulier
                    backend(proxy(request).uucp.purge_actions([id_serveur]))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("Erreur lors de la purge des actions<br>")


            self.content = """<p>Purge des actions terminées.<br/>
            <a href="javascript:history.back()">Retour à la page des actions</a><br/>
            </p>"""

        except Exception as e:
            self.content = e

        return self.content

class GetDico(Design):
    """Préparation du téléchargement du fichier de configuration d'un serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"get_dico" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Téléchargement de la configuration du serveur"

    def wmfactory_content(self, request):
        return self.content

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html#section4").menu()

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("id du serveur")
            try:
                mode = escape(request.args[b'mode'][0])
                assert mode in ["modif_config","modif_migration","modif_dico"]
            except Exception:
                raise FrontendError("nom de fichier")

            try:
                if mode.endswith('migration'):
                    recup_dico = backend(proxy(request).serveurs.migrate_conf(int(id_serveur),u(mode),''))
                    if type(recup_dico[-1]) == dict and 'load_errors' in recup_dico[-1]:
                        # On supprime les éventuelles infos de migration remontées dans le cas de creole3
                        del(recup_dico[-1])
                else:
                    recup_dico = backend(proxy(request).serveurs.get_dico(int(id_serveur),u(mode),False,True))
                # détermine la version du serveur
                # (nécessaire pour creole3: https://dev-eole.ac-dijon.fr/issues/10530)
                server_version = backend(proxy(request).serveurs.module_version(int(id_serveur)))
                # version de la configuration à éditer
                eole_version = server_version
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                traceback.print_exc()
                raise BackendError(e)
            try:
                if mode.endswith('migration'):
                    # configuration de migration, on détecte la version de creole vers laquelle migrer
                    variante_dest = backend(proxy(request).serveurs.variante_migration(int(id_serveur)))
                    assert int(variante_dest) >= 0
                    var_info = backend(proxy(request).modules.get_variante(variante_dest))[0]
                    mod_dest = backend(proxy(request).modules.get_module(var_info['module']))[0]
                    # version de la configuration de migration
                    eole_version =  int(mod_dest['version'])
                    creole_version = config.CREOLE_VERSIONS[eole_version]
                else:
                    infos_serveur = backend(proxy(request).serveurs.get_serveur(int(id_serveur)))
                    creole_version = backend(proxy(request).serveurs.creole_version(int(id_serveur)))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            dico_eole = ZephirDict(mode='', version=creole_version, eole_version=eole_version, server_version=server_version)
            dico_eole.init_from_zephir(recup_dico)
            # écriture du fichier à télécharger dans PATH_TMP
            type_fic = mode.replace('modif_','')
            filename = "zephir_%s_%s.eol" % (type_fic, str(id_serveur))
            # on vérifie que le répertoire temporaire existe
            if not os.path.isdir(config.PATH_TEMP):
                os.makedirs(config.PATH_TEMP)
            f_temp = os.path.join(config.PATH_TEMP,filename)
            dico_eole.save(f_temp,force=True)
            self.content += """
            <p><a href="/tmp/%s">Lien temporaire vers le fichier de configuration</a></p>
            <p>(faire un clic droit → Enregistrer la cible du lien sous…)</p>
            <p><a href="javascript:history.back()">Retour vers la page d'état du serveur</a></p>
            """ % filename

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class MigrateConf(Design):
    """Migration - choix de la variante"""
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"migrate_conf" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Migration de serveur - choix de la variante"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # réception des variables de formulaire
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
            except Exception:
                raise FrontendError("id")
            try:
                assert id_serveur
            except AssertionError:
                raise FrontendError("id")
            mode = "migration"
            try:
                # recherche des variantes disponibles
                serveurs = backend(proxy(request).serveurs.get_serveur(int(id_serveur)))
                creole_version = backend(proxy(request).serveurs.creole_version(int(id_serveur)))
                modules = backend(proxy(request).modules.get_module())
                mod_old = serveurs[0]['module_actuel']
                for mod in modules:
                    if mod['id'] == mod_old:
                        lib_mod = mod['libelle']
                        lib_mod = lib_mod[:lib_mod.rindex('-')]
                        vers_mod = mod['version']
                        break
                # récupération de la liste des modules autorisés pour la migration
                new_versions = config.allowed_migrations[vers_mod]
                mods_new = {}
                for mod in modules:
                    lib = mod['libelle']
                    vers = mod['version']
                    lib = lib[:lib.rindex('-')]
                    # on propose seulement les modules encore maintenus
                    if vers in config.DISTRIBS and config.DISTRIBS[vers][2] is True:
                        if lib == lib_mod and vers in new_versions:
                            mods_new[mod['id']] = (vers, mod['libelle'])
                if mods_new == {}:
                    raise Exception('module de destination non trouvé')
                variantes = backend(proxy(request).modules.get_variante())
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                traceback.print_exc()
                raise BackendError(e)

            self.content ="""<script language="JavaScript"><!--
            function valid_variante(form)
            {
                //getting the values:
                creole_version = form.sel_variante.value.split('@')[0];
                var_migration = form.sel_variante.value.split('@')[1];
                //setting variante id in form
                form.variante.value = var_migration;
                if (creole_version === "creole3")
                {
                    window.open('/genconfig1/migrate/%s/'+var_migration+'/migration','site','scrollbars=yes, resizable=yes, location=no, directories=no, status=no');
                    return false;
                } else {
                    window.open('/genconfig/migrate/%s/'+var_migration+'/migration','site','scrollbars=yes, resizable=yes, location=no, directories=no, status=no');
                    return false;
                }
            }
            //--></script>""" % (str(id_serveur), str(id_serveur))
            self.content += """<b>Préparation de la migration</b><br/>
            Choisissez la variante à utiliser lors de la migration du serveur\n"""
            self.content += """<p><FORM METHOD="POST" ACTION="/serveur/dico" onSubmit="return valid_variante(this)">"""
            self.content += """<input type="hidden" name="id" value=%s>
            <input type="hidden" name="variante" value="">
            <input type="hidden" name="mode" value=%s>""" % (id_serveur, mode)
            self.content += """<select name="sel_variante">\n"""
            sel_ok = False
            for variante in variantes:
                selected = ""
                if variante['module'] in mods_new:
                    if variante['libelle'] == "standard" and not sel_ok:
                        selected = " selected"
                        sel_ok = True
                    mod_vers, mod_libelle = mods_new[variante['module']]
                    creole_vers = config.CREOLE_VERSIONS[mod_vers]
                    self.content += """<OPTION value=%s@%s%s>%s - %s</OPTION>\n""" % (creole_vers, str(variante['id']), selected, mod_libelle, variante['libelle'])
            self.content += """</select><input type=SUBMIT value="Générer la configuration"></FORM></p>\n"""
            self.content += """<br/><a href="/serveur/etat?id=%s">Retour à la page d'état du serveur</a><br><br>""" % (id_serveur)

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class PersoServeur(Design):
    """Affichage des fichiers de personnalisation du serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"perso" :
            return self
        return Serveur()

    def wmfactory_menu(self, request):
        return Navigation(aide="howto/page6.html#section3").menu()

    def wmfactory_title(self,request):
        return "Fichiers spécifiques du serveur"

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self, fichiers, id_serveur, dicos, available, detected, removed_exists=False):
        """Affichage de la liste des différents fichiers de personnalisations
        """
        s = """"""
        # constitution de l'affichage des fichiers (plus lien de suppression)
        affiche={}
        get_dictname = 'false'
        if dicos is not None:
            # listes des dictionnaires actifs/disponibles
            dic_serv, dic_def = gen_liste_dicos(dicos['serveur'], [])
            used_dicts = dicos['serveur'] + dicos['variante'] + dicos['module']
            if detected:
                if len(detected) == 1:
                    infos = {'pluriel':"", 'nb':1}
                else:
                    infos = {'pluriel':"s", 'nb':len(detected)}
                    label = "%d paquets non activés" % len(detected)
                warn_detected = """<br/><br/><font color="red">%(nb)d paquet%(pluriel)s non activé%(pluriel)s&nbsp;:&nbsp;</font>Il est recommandé d'activer<br/>
le%(pluriel)s paquet%(pluriel)s manquant%(pluriel)s (ici ou au niveau de la variante)<br/>
et de vérifier la configuration avant tout envoi au serveur<br/>""" % infos
            else:
                warn_detected = ""
                warn_help = ""
            add_dict_form = """<br/><select name="add_dict">%s</select>%s""" % (gen_available_dicos(used_dicts, available, detected), warn_detected)
        else:
            add_dict_form = """<br/><INPUT NAME="dico" TYPE="file"/>"""
            get_dictname = 'true'

        # fichiers spécifiques au serveur
        for type_f in ['dicos','persos','patchs','fichiers_zeph','rpms']:
            l=["""<table width="100%">"""]
            if type_f == 'dicos' and dicos is not None:
                # si des dictionnaires sont définis, on affiche la liste
                if dic_serv:
                    # gestion de l'affichage avec le pool de dictionnaires
                    form_dic_var = """%s<br><br>(Cocher pour supprimer)""" % "<br/>".join(dic_serv)
                    l.append(form_dic_var)
            else:
                # stockage des fichiers_divers par conteneur pour simplifier l'affichage
                container_files = {'':[]}
                bad_files = {'':[]}
                liste_fichiers = fichiers[type_f]
                # tri des fichiers
                liste_fichiers.sort(key=lambda x:x[0])
                ask_remove = "true"
                if type_f not in ('fichiers_zeph', 'persos'):
                    ask_remove = "false"
                for f in liste_fichiers:
                    if type_f != 'rpms':
                        # fichiers éditables
                        if type_f == 'fichiers_zeph':
                            container = ''
                            # cas particulier des fichiers divers : icone selon le type et la présence ou non
                            f, f_info = f
                            if ('::') in f:
                                # gestion des conteneurs
                                if f.split('::')[1] not in ('root', ''):
                                    f_name, container = f.split('::')
                                else:
                                    f_name = f.split('::')[0]
                            else:
                                f_name = f
                            f_img = config.file_icons.get(f_info,'gnome-mime-text.png')
                            if f_info == 'missing':
                                # fichier non présent sur zephir
                                if container not in bad_files:
                                    bad_files[container] = ["""<tr><td colspan="2"><b>conteneur %s</b></td></tr>""" % container]
                                bad_files[container].append("""<tr><td><img border="0" src="/images/%s">%s</td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (f_img,f_name,f,type_f,ask_remove))
                            else:
                                if container not in container_files:
                                    container_files[container] = ["""<tr><td colspan="2"><b>conteneur %s</b></td></tr>""" % container]
                                container_files[container].append("""<tr><td><img border="0" src="/images/%s"><a href="edit_fichier?id=%s&file=%s&type=%s">%s</a></td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (f_img,id_serveur,f,type_f,f_name,f,type_f, ask_remove))
                        else:
                            l.append("""<tr><td><a href="edit_fichier?id=%s&file=%s&type=%s">%s</a></td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (id_serveur,f,type_f,f,f,type_f,ask_remove))
                    else:
                        # paquets: on ne propose pas la suppression ici pour les paquets de dictionnaires
                        if dicos is not None and f in [dico.rsplit(os.sep, 2)[-2] for dico in dicos['serveur']]:
                            l.append("""<tr><td colspan="2">"""+f+"</td></tr>")
                        else:
                            # l.append("""<tr><td>%s</td><td align="right">(<a href="/serveur/perso?id=%s&fic_sup=%s&type=%s">supprimer</a>)</td></tr>""" % (f,id_serveur,f,type_f))
                            l.append("""<tr><td>%s</td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (f,f,type_f,ask_remove))
                if type_f in ('fichiers_zeph'):
                    # affichage de la liste des fichiers divers (par conteneur si besoin)
                    liste_cont = list(container_files.keys())
                    # on trie pour avoir le conteneur root en premier
                    liste_cont.sort()
                    for container in liste_cont:
                        l.extend(container_files[container])
                    if not (len(bad_files) == 1 and bad_files[''] == []):
                        l.append("""<tr><td colspan="2"><table width="100%"><tr><td width="50%"><hr></td><td nowrap="1">Fichiers absents</td><td width="50%"><hr></td></tr></table></td></tr>""")
                        liste_cont = list(bad_files.keys())
                        # on trie pour avoir le conteneur root en premier
                        liste_cont.sort()
                        for container in liste_cont:
                            l.extend(bad_files[container])
            l.append("</table>")
            affiche[type_f]=l

        # fichiers de la variante (affichage seulement)
        for type_f in ['dicos_var','persos_var','patchs_var','fichiers_var','rpms_var']:
            l_var=["""<table width="100%">"""]
            container_files = {'':[]}
            liste_fichiers = fichiers[type_f]
            # tri des fichiers
            liste_fichiers.sort(key=lambda x:x[0])
            for f in liste_fichiers:
                if type_f == 'fichiers_var':
                    # cas particulier des fichiers divers : icone selon le type et la présence ou non
                    f, f_info = f
                    # gestion des conteneurs
                    container = ""
                    container_var_info = '<td></td>'
                    if ('::') in f:
                        if f.split('::')[1] not in ('root', ''):
                            f, container = f.split('::')
                        else:
                            f = f.split('::')[0]
                    f_img = config.file_icons.get(f_info,'gnome-mime-text.png')
                    if container not in container_files:
                        container_files[container] = ["""<tr><td><b>conteneur %s</b></td></tr>""" % container]
                    container_files[container].append("""<tr><td><img border="0" src="/images/%s">%s</td></tr>""" % (f_img,f))
                else:
                    l_var.append("""<tr><td>%s</td></tr>""" % f)
            if type_f in ('fichiers_var'):
                liste_cont = list(container_files.keys())
                # on trie pour avoir le conteneur root en premier
                liste_cont.sort()
                for container in liste_cont:
                    l_var.extend(container_files[container])
            l_var.append("</table>")
            affiche[type_f]=l_var
        container_input = """<br><input type="text" name="fichier_container" value="">conteneur (facultatif)"""
        if removed_exists:
            removed_input = """<tr><td colspan="4" align="center"><a href="edit_fichier?id=%s&file=removed&type=%s&localpath=%s">\
Fichiers à supprimer sur le client</a> (templates et fichiers divers)</td></tr>\
""" % (id_serveur, "fichiers_zeph", "fichiers_zephir")
        else:
            removed_input = ""

        if dicos is not None:
            s += """
<style type="text/css">
td.fichiers_module {
  display:table-cell;
}
</style>"""
            # dictionnaires actifs pour variante et module, mais non modifiables
            dic_var = gen_liste_dicos(dicos['variante'], dicos['variante'], "black")[1]
            affiche['dicos_var'] = "<br>".join(dic_var)
            # pour le module, on n'affiche pas les dictionnaires par défaut
            # du module pour ne pas surcharger l'affichage
            perso_module = list(set(dicos['module']).difference(set(dicos['default'])))
            dic_mod  = gen_liste_dicos(perso_module, perso_module, "black")[1]
            affiche['dicos_mod'] = "<br>".join(dic_mod)
            # on reprend la liste des paquets de dictionnaires du module
            rpms_mod = set([paq.split(os.sep)[-2] for paq in perso_module if not paq.split(os.sep)[-2] in ('eole', 'local')])
            affiche['rpms_mod'] = "<br>".join(rpms_mod)
        else:
            affiche['dicos_mod'] = []
            affiche['rpms_mod'] = []

        return s + """
<table border="1" cellpadding="5" cellspacing="0" align="center" valign="middle" >
<script language="JavaScript"><!--
function del_file(fic,type_fic,ask_remove)
{
    var del_form = document.forms['delform'];
    del_form.fic_sup.value = fic;
    del_form.type.value = type_fic;
    // on demande si le fichier doit être supprimé sur le client
    if ( ask_remove == true ) {
        if (confirm('Supprimer également le fichier sur la machine cible ?\\n(Annuler le supprimera seulement sur Zéphir)'))
        {
            del_form.remove.value="true";
        }
    }
    del_form.submit();
    return location;
};
function get_names (get_dictname)
{
    //getting the values:
    var val_template = document.file_form.template.value;
    var val_patch = document.file_form.patch.value;
    //setting the values:
    document.file_form.template_name.value = val_template;
    document.file_form.patch_name.value = val_patch;
    if (get_dictname) {
        var val_dico = document.file_form.dico.value;
        document.file_form.dico_name.value = val_dico;
    }
};
//--></script>
<FORM METHOD="POST" NAME="delform" ACTION="perso">
<INPUT TYPE="hidden" NAME="id" VALUE="%s">
<INPUT TYPE="hidden" NAME="fic_sup" VALUE="">
<INPUT TYPE="hidden" NAME="type" VALUE="">
<INPUT TYPE="hidden" NAME="remove" VALUE="false">
</FORM>

<FORM name="file_form" METHOD="POST" ACTION="perso" enctype="multipart/form-data" onSubmit="javascript:get_names(%s)">
<tr><td align="center">Liste des fichiers</td><td align="center">Serveur</td><td align="center">Variante</td><td align="center" class="fichiers_module">Module</td></tr>%s
<tr><td valign="top">Dictionnaires additionnels%s</td><td valign="top">%s</td><td valign="top">%s</td><td valign="top" class="fichiers_module">%s</td></tr>
<tr><td valign="top">Templates additionnels<br/><INPUT NAME="template" TYPE="file"/></td><td valign="top">%s</td><td valign="top">%s</td><td class="fichiers_module"></td></tr>
<tr><td valign="top">Patchs<br/><INPUT NAME="patch" TYPE="file"/></td><td valign="top">%s</td><td valign="top">%s</td><td class="fichiers_module"></td></tr>
<tr><td valign="top">Fichiers divers<br/><INPUT NAME="fichier" TYPE="file"/><br><input type="text" name="fichier_name" value="">destination
%s</td><td valign="top">%s</td><td valign="top">%s</td><td class="fichiers_module"></td></tr>
<tr><td valign="top">Paquets additionnels<br/><INPUT NAME="rpms" TYPE="text"/></td><td valign="top">%s</td><td valign="top">%s</td><td valign="top" class="fichiers_module">%s</td></tr>
<input type="hidden" name="dico_name" value="">
<input type="hidden" name="template_name" value="">
<input type="hidden" name="patch_name" value="">
<tr><td colspan="4" align = "center"><input type="hidden" name="id" value="%s">
<input type="hidden" name="fichiers" value="oui">
<input type=submit value="Appliquer les modifications"><br>
<input name="initialiser" value="Réinitialiser" type="reset"/></td></tr>
</form>
</table><br>
""" % (id_serveur,get_dictname,removed_input,add_dict_form,"".join(affiche['dicos']),"".join(affiche['dicos_var']),"".join(affiche['dicos_mod']),"".join(affiche['persos']),"".join(affiche['persos_var']),"".join(affiche['patchs']),"".join(affiche['patchs_var']),container_input,"".join(affiche['fichiers_zeph']),"".join(affiche['fichiers_var']),"".join(affiche['rpms']),"".join(affiche['rpms_var']), "".join(affiche['rpms_mod']), id_serveur) + """<p><a href="/serveur/del_perms?id=%s">Voir les permissions définies</a></p><p><a href="etat?id=%s">Retour à la page d'état</a></p>""" % (id_serveur, id_serveur)

    @defer.inlineCallbacks
    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except Exception:
                raise FrontendError("identifiant du serveur")
            # lecture des infos sur le module du serveur
            infos_serv = backend(proxy(request).serveurs.get_serveur(int(id_serveur)))[0]
            mod_actuel = int(infos_serv['module_actuel'])
            id_variante = int(infos_serv['variante'])
            mod_version = int(backend(proxy(request).modules.get_module(mod_actuel))[0]['version'])
            # on regarde si on doit supprimer un fichier
            try:
                fic_sup=escape(request.args[b'fic_sup'][0])
                type_sup=escape(request.args[b'type'][0])
                # remove_sup : non présent dans le cas des rpms/dicos
                remove_sup = escape(request.args.get(b'remove', ['false'])[0]) == 'true'
                if b'localpath' in request.args:
                    localpath = escape(request.args[b'localpath'][0])
                else:
                    localpath = ""
            except:
                for param in request.args:
                    param = param.decode()
                    if param.startswith('removedict_'):
                        try:
                            prefix, dict_type, dict_name = param.split('_', 2)
                            # vérification du mot de passe/utilisateur
                            backend(proxy(request).dicos.del_serveur(int(id_serveur), dict_type, dict_name))
                        except Exception as e:
                            log.msg("Erreur lors de la supression d'un dictionnaire du serveur %s: %s" % (str(id_serveur), str(e)))
                    elif param == 'add_dict' and request.args.get(b'add_dict',[b''])[0].decode() != '':
                        # ajout d'un dictionnaire à la variante
                        dict_type, dict_name = escape(request.args[b'add_dict'][0]).split('_', 1)
                        backend(proxy(request).dicos.add_serveur(int(id_serveur), dict_type, dict_name))
                # on regarde si on doit ajouter des fichiers
                try:
                    escape(request.args[b'fichiers'][0])
                except:
                    # on arrive depuis une autre page
                    pass
                else:
                    # on a demandé un ajout de fichiers
                    dic_files = {'dicos':[],'patchs':[],'persos':[],'fichiers_zeph':[],'rpms':[]}
                    # on récupère les noms et contenus des fichiers
                    try:
                        dic_files['dicos'].append([escape(request.args[b'dico_name'][0]),base64.encodebytes(request.args[b'dico'][0])])
                    except:
                        pass
                    try:
                        dic_files['persos'].append([escape(request.args[b'template_name'][0]),base64.encodebytes(request.args[b'template'][0])])
                    except:
                        pass
                    try:
                        dic_files['patchs'].append([escape(request.args[b'patch_name'][0]),base64.encodebytes(request.args[b'patch'][0])])
                    except:
                        pass
                    try:
                        # on regarde si un conteneur est spécifié
                        container = ''
                        if request.args.get(b'fichier_container', [b''])[0].decode() not in ('', 'root'):
                            container = '::%s' % request.args[b'fichier_container'][0].decode()
                        # ajout d'un 'fichier divers'
                        dic_files['fichiers_zeph'].append([escape(request.args[b'fichier_name'][0].decode() + container),base64.encodebytes(request.args[b'fichier'][0])])
                    except:
                        pass
                    else:
                        # il faut donner un chemin absolu
                        if escape(request.args[b'fichier_name'][0]) != '' and not escape(request.args[b'fichier_name'][0]).startswith('/'):
                            raise FrontendError("""le chemin du fichier doit être absolu (ex : /tmp/toto)""")
                    try:
                        # XXX FIXME : paquets de dictionnaires liés aux modules : non affichés
                        # dans la section des paquets additionnels
                        dic_files['rpms'].append(escape(request.args[b'rpms'][0]))
                    except:
                        pass
                    try:
                        server_mod_paqs = backend(proxy(request).dicos.list_module(mod_actuel, False))
                    except:
                        server_mod_paqs = []
                    # mise en place des fichier
                    backend(proxy(request).serveurs.add_files(int(id_serveur),dic_files))
            else:
                # on demande la suppression du fichier
                if localpath:
                    fic_sup = [fic_sup, localpath]
                dic_files = {type_sup:[fic_sup]}
                # on supprime
                try:
                    # appel avec yield pour être sûr qu'on attende le retour de la fonction avant la suite
                    del_ok = yield backend(proxy(request).serveurs.del_files(int(id_serveur),dic_files,remove_sup))
                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except Exception as e:
                    raise BackendError(e)
            try:
                # récupération de la liste des dictionnaires disponibles/utilisés dans dictpool
                # dicos utilisés au niveau module
                dicos_def = backend(proxy(request).dicos.get_module_defaults(mod_actuel))
                dicos_mod = backend(proxy(request).dicos.list_module(mod_actuel))
                # dicos utilisés au niveau variante
                dicos_var = backend(proxy(request).dicos.list_variante(id_variante))
                # dicos utilisés au niveau seveur
                dicos_serv = backend(proxy(request).dicos.list_serveur(int(id_serveur)))
                available = backend(proxy(request).dicos.list_available(mod_version))
                # paquets de dictionnaires installés mais non activés
                detected = backend(proxy(request).serveurs.get_status(int(id_serveur)))['dictpaqs_ok']
                if detected[0] == 0:
                    # on ne garde que la liste des paquets détectés du tuple (etat, liste_paqs)
                    detected = detected[1]
                else:
                    detected = []
                dicos = {'serveur':dicos_serv,'variante':dicos_var,'module':dicos_mod,'default':dicos_def}
                # on recharge la liste des fichiers
                dico_fic = backend(proxy(request).serveurs.fichiers_zephir(int(id_serveur), True))
                # on regarde si le fichier fichiers_zephir/removed est présent
                try:
                    backend(proxy(request).serveurs.get_file_content(int(id_serveur), 'fichiers_zephir/removed'), False)
                    removed_exists = True
                except:
                    removed_exists = False
            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except Exception as e:
                raise BackendError(e)
            self.content = """<h1>Liste des fichiers personnalisés</h1>%s""" % self._dump_html(dico_fic, id_serveur, dicos, available, detected, removed_exists)

        except (FrontendError,BackendError) as e:
            self.content = str(e)

        defer.returnValue(self.content)

class EditFichier(Design):
    """Edition d'un fichier de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"edit_fichier" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Édition (fichier de serveur)"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            # définition du chemin du fichier en fonction de son type
            chemins={'dicos':'dicos/local/',
                    'persos':'fichiers_perso/',
                    'patchs':'patchs/',
                    'fichiers_zeph':'fichiers_zephir/'
            }
            try:
                filename = escape(request.args[b'file'][0])
                id_serveur = int(escape(request.args[b'id'][0]))
                type_file = escape(request.args[b'type'][0])
                assert filename
                assert id_serveur
                assert type_file in list(chemins.keys())
            except:
                raise FrontendError
            try:
                localpath = escape(request.args[b'localpath'][0])
            except:
                localpath = ""
            basepath=localpath
            # version du serveur
            detail_serv = backend(proxy(request).serveurs.get_serveur(id_serveur))
            serv_mod = int(detail_serv[0]['module_actuel'])
            detail_mod = backend(proxy(request).modules.get_module(serv_mod))
            mod_version = int(detail_mod[0]['version'])
            # récupération du contenu
            if localpath != "":
                path = localpath + os.sep + os.path.basename(filename)
            else:
                path = chemins[type_file] + os.path.basename(filename)
            localpath = path
            contenu = backend(proxy(request).serveurs.get_file_content(id_serveur,path,True))
            self.content = """<br/><a href=/serveur/perso?id=%s>Retour</a>""" % str(id_serveur)
            # gestion des droits pour les fichiers personnalisés (pas dicos et patchs)
            if (type_file  == 'persos') and (mod_version > 1):
                # les droits sont gérés au niveau du dictionnaire
                self.content += """<center><br/><font color="blue">Les droits à appliquer sur les templates personnalisés
                doivent être défini dans le dictionnaire correspondant</font><br/>
                exemple:&nbsp;&lt;file name='/etc/ldap/slapd.conf' owner="openldap" group="openldap" mode="0640"/&gt;"""
            elif type_file == 'fichiers_zeph':
                # récupération des droits actuels du fichier
                file_rights = backend(proxy(request).serveurs.get_serveur_perms(id_serveur, path))
                if file_rights[path] != []:
                    mode, user, group, recursive = file_rights[path]
                if recursive == True:
                    checked = " CHECKED"
                else:
                    checked = ""
                # formulaire de modification des droits/utilisateurs
                if type(contenu) == list:
                    recurse_label = "<td>recursif</td>"
                    recurse_input = """<td align="center"><INPUT TYPE="checkbox" NAME="recurse"%s></td>""" % checked
                else:
                    recurse_label = recurse_input = ""
                if ('::') in path:
                    path_info = "%s (conteneur %s)" % (path.split('::')[0], path.split('::')[-1])
                else:
                    path_info = path
                self.content += """
<script language="JavaScript"><!--
function del_file(fic,type_fic,ask_remove)
{
    var del_form = document.forms['delform'];
    del_form.fic_sup.value = fic;
    del_form.type.value = type_fic;
    // on demande si le fichier doit être supprimé sur le client
    if ( ask_remove == true ) {
        if (confirm('Supprimer également le fichier sur la machines cibles ?'))
        {
            del_form.remove.value="true";
        }
    }
    del_form.submit();
    return location;
    };
//--></script>
<table border="1">
<FORM METHOD="POST" ACTION="save_perms">
<INPUT TYPE="hidden" NAME="filename" value="%s">
<INPUT TYPE="hidden" NAME="id" value="%s">
<tr><td colspan="2" align="center">Modification des droits de <b>%s</b></td></tr>
<tr><td><table><tr><td>mode</td></tr><tr><td><INPUT TYPE="text" NAME="mode" SIZE="4" value="%s"></td></tr></table></td>
<td><table><tr><td>utilisateur</td><td>groupe</td>%s</tr>
<tr><td><INPUT TYPE="text" NAME="user" value="%s"></td><td><INPUT TYPE="text" NAME="group" value="%s"></td>%s</tr>
</table></td></tr><tr><td align="center" colspan="2">
<INPUT TYPE="Submit" value="appliquer les permissions"/></FORM>
<FORM METHOD="POST" NAME="delform" ACTION="perso">
<INPUT TYPE="hidden" NAME="id" VALUE="%s">
<INPUT TYPE="hidden" NAME="fic_sup" VALUE="">
<INPUT TYPE="hidden" NAME="type" VALUE="">
<INPUT TYPE="hidden" NAME="localpath" VALUE="%s">
<INPUT TYPE="hidden" NAME="remove" VALUE="false">
</FORM>
</td></tr></table>""" % (path, id_serveur, path_info, mode, recurse_label, user, group, recurse_input, id_serveur, localpath)
            # gestion du contenu du fichier/répertoire
            if type(contenu) == list:
                # cas d'un répertoire, on affiche le contenu
                self.content +=  """<p><b>Contenu du répertoire</b></p><table border="1"><tr><td><table>"""
                for fic, f_info in contenu:
                    f_img = config.file_icons.get(f_info,'gnome-mime-text.png')
                    if f_info == 'missing':
                        # fichier non présent sur zephir
                        self.content += """<tr><td><img border="0" src="/images/%s">%s</td>\n""" % (f_img, fic)
                    else:
                        self.content += """<tr><td><img border="0" src="/images/%s"><a href="edit_fichier?id=%s&file=%s&type=%s&localpath=%s">%s</a></td>\n""" % (f_img, id_serveur,os.path.join(filename,fic),type_file,localpath,fic)
                    ask_remove = "true"
                    if mod_version < 5 or type_file not in ('fichiers_zeph', 'persos'):
                        # eole2.2 et > : on gère la suppression du fichier sur le client
                        ask_remove = "false"
                    self.content += """<td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>\n""" % (os.path.join(filename,fic), type_file, ask_remove)
                self.content += """</table></td></tr></table><br/>"""
            else:
                if contenu == "BINARY":
                    self.content += """<p>fichier non éditable (binaire)</p>"""
                else:
                    # formulaire de modification du contenu
                    self.content += """<FORM METHOD="POST" ACTION="save_file">
<INPUT TYPE="hidden" NAME="filename" value="%s">
<INPUT TYPE="hidden" NAME="id" value="%s">
<INPUT TYPE="hidden" NAME="type" value="%s">
<INPUT TYPE="hidden" NAME="localpath" value="%s"><BR/>
<textarea wrap="off" NAME="page" COLS="80" ROWS="16" STYLE="width: 90%%; heigth: 80%%">%s</textarea>
<p><INPUT TYPE="Submit" value="Sauvegarder le contenu"/></p></FORM>
""" % (filename,id_serveur,type_file,basepath,base64.decodebytes(contenu.encode()).decode())

        except Exception as e:
            self.content = e

        return self.content

class DeletePerms(Design):
    """Edition du fichier de permissions d'un serveur (fichiers personnalisés)
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"del_perms" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Édition des permissions"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                assert id_serveur
            except:
                raise FrontendError
            try:
                delpath=escape(request.args[b'del_path'][0])
            except:
                delpath=None
            # suppression des fichiers demandés
            try:
                if delpath != None:
                    if delpath == "droits_zephir":
                        backend(proxy(request).serveurs.del_serveur_perms(id_serveur))
                    else:
                        backend(proxy(request).serveurs.del_serveur_perms(id_serveur,delpath))
                # récupération du contenu
                permissions=backend(proxy(request).serveurs.get_serveur_perms(id_serveur))
                info_serveur=backend(proxy(request).serveurs.get_serveur(id_serveur))
            except:
                raise BackendError

            # génération du tableau des permissions existantes
            #self.content = """<p><a href=/serveur/perso?id=%s>Retour</a></p>""" % id_serveur
            self.content = """<h1>Permissions définies sur le serveur %s</h1>""" % id_serveur
            if len(permissions) > 0:
                self.content += """<table border="1">
                <tr><td colspan="6" align="center"><b>Permissions définies sur le serveur %s (%s)</td></tr>
                <tr><td>nom du fichier sur zephir</td><td>permissions</td><td>utilisateur</td><td>groupe</td><td>recursif</td><td></td></tr>""" % (info_serveur[0]['libelle'], id_serveur)
                for ficperso, data in list(permissions.items()):
                    check = ""
                    try:
                        perms, user, group, recurse = data
                        if recurse == True:
                            check = " checked"
                    except:
                        # droits non valides, ne devrait pas arriver
                        self.content += """<tr><td>%s</td><td colspan="4">Erreur de lecture des droits</td></tr>""" % ficperso
                    self.content += """<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td align="center"><input type="checkbox" disabled %s></td><td><a href="/serveur/del_perms?id=%s&del_path=%s">supprimer</a></td></tr>""" % (ficperso, perms, user, group, check, id_serveur, ficperso)
                self.content += """<tr><td colspan="6" align="center"><a href="/serveur/del_perms?id=%s&del_path=droits_zephir">Tout supprimer</a></td></tr></table>""" % (id_serveur)
            else:
                self.content += """<p><span id="message">Pas de permission spécifiée pour ce serveur</span></p>"""
                self.content += """<p><a href=/serveur/perso?id=%s>Retour</a></p>""" % id_serveur
        except Exception as e:
            self.content = e

        return self.content

class SaveFile(Design):
    """sauvegarde du fichier d'un serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"save_file" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Sauvegarde (fichier de serveur)"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                filename = escape(request.args[b'filename'][0])
                id_serveur = escape(request.args[b'id'][0])
                contenu = request.args[b'page'][0]
                type_file = escape(request.args[b'type'][0])
                assert id_serveur
                assert filename
            except:
                raise FrontendError
            if b'localpath' in request.args:
                localpath = request.args[b'localpath'][0]
            else:
                localpath = ""

            # sauvegarde du fichier sur zephir
            dic_files = {'dicos':[],'patchs':[],'persos':[],'fichiers_zeph':[],'rpms':[]}
            # on strippe les retour de ligne 'windows'
            win_new_line = chr(13)+chr(10)
            contenu=contenu.replace(win_new_line.encode(),b'\n')
            dic_files[type_file].append([filename,base64.encodebytes(contenu),localpath])

            # mise en place des fichier
            backend(proxy(request).serveurs.add_files(int(id_serveur),dic_files,True))

            self.content = """Fichier sauvegardé<BR>
            <a href=/serveur/perso?id=%s>Retour</a>""" % (id_serveur)

        except Exception as e:
            traceback.print_exc()
            self.content = e

        return self.content

class SavePerms(Design):
    """sauvegarde des permissions pour un fichier du serveur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"save_perms" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Sauvegarde des permissions"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                filename = escape(request.args[b'filename'][0])
                id_serveur = escape(request.args[b'id'][0])
                mode = escape(request.args[b'mode'][0])
                user = escape(request.args[b'user'][0])
                group = escape(request.args[b'group'][0])
                if b'recurse' in request.args:
                    recursive = True
                else:
                    recursive = False
                assert id_serveur, filename
            except:
                raise FrontendError

            # sauvegarde des permissions sur zephir
            rights = {filename:[mode, user, group,recursive]}
            backend(proxy(request).serveurs.set_serveur_perms(int(id_serveur),u(rights)))

            self.content = """Permissions sauvegardées<br />
            <a href=/serveur/perso?id=%s>Retour</a>""" % (id_serveur)

        except Exception as e:
            self.content = e

        return self.content

class CopyPerms(Design):
    """Copie les permissions d'un serveur (droits_zephir)
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"copy_perms" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Copies des permissions"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                id_serveur = escape(request.args[b'id'][0])
                id_src = escape(request.args[b'id_src'][0])
                keep = False
                if b'keep' in request.args:
                    keep = True
                id_serveur = int(id_serveur)
                id_src = int(id_src)
            except:
                raise FrontendError
            # copie des permissions sur zephir
            # préparation de la liste des destinations
            try:
                if id_serveur == -1:
                    # on récupère la liste des ids de serveurs
                    serveurs = [ i['id'] for i in request.getSession().groupe_serveur]
                else:
                    serveurs = [id_serveur]
            except :
                raise FrontendError("groupe de serveurs")
            # copie sur zephir
            try:
                res = backend(proxy(request).serveurs.copy_perms(int(id_src),u(serveurs),keep))
            except:
                raise BackendError
            self.content = """Permissions copiées<BR>"""
            if res != []:
                self.content += """Les permissions n'ont pas pu être modifiées pour les serveurs suivants (accès refusé):<br>%s""" \
                        % ", ".join(res)
            self.content += """<a href="javascript:history.back()">Retour à la page des actions</a><br/>"""

        except Exception as e:
            self.content = e

        return self.content

class GroupePerms(Design):
    """supprime des permissions sur un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"groupe_perms" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression de permissions sur un groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            id_groupe = None
            try:
                id_groupe = escape(request.args[b'id_groupe'][0])
            except:
                pass
            try:
                # liste des serveurs à modifier
                serveurs = [ i['id'] for i in request.getSession().groupe_serveur]
                assert serveurs != []
            except Exception:
                raise FrontendError("liste des serveurs du groupe")
            try:
                delpath=escape(request.args[b'del_path'][0])
            except:
                delpath=None
            try:
                # suppression des fichiers demandés
                if delpath != None:
                    for id_serv in serveurs:
                        if delpath == "droits_zephir":
                            backend(proxy(request).serveurs.del_serveur_perms(id_serv))
                        else:
                            backend(proxy(request).serveurs.del_serveur_perms(id_serv,delpath))
                # récupération du contenu
                permissions = {}
                keys = []
                for id_serv in serveurs:
                    perms=backend(proxy(request).serveurs.get_serveur_perms(id_serv))
                    for perm,data in list(perms.items()):
                        if data[3] == True:
                            data[3] = "récursif"
                        else:
                            data[3] = ""
                        if perm not in keys:
                            keys.append(perm)
                            permissions[perm]=[1,[data]]
                        else:
                            if data not in permissions[perm][1]:
                                # liste des occurences
                                permissions[perm][1].append(data)
                            # nb total d'occurences
                            permissions[perm][0] += 1
            except:
                raise BackendError

            # génération du tableau des permissions existantes
            gr_string = ""
            if id_groupe != None:
                gr_string = "&id_groupe=%s" % str(id_groupe)
            self.content = """<p><a href=/serveur/action?id=-1%s>Retour</a></p>""" % gr_string
            if len(permissions) > 0:
                self.content += """<table border="1">
                <tr><td colspan="4" align="center"><b>Permissions définies sur le groupe </td></tr>
                <tr><td>nom du fichier sur zephir</td><td>valeurs existantes</td><td>occurences</td><td></td></tr>"""
                for ficperso, data in list(permissions.items()):
                    occurences, droits = data
                    choice = """<select>"""
                    for val in droits:
                        if "" in val:
                            val.remove("")
                        choice += """<option>%s</option>""" % ", ".join(val)
                    choice += """</select>"""
                    self.content += """<tr><td>%s</td><td>%s</td><td>%s</td><td><a href="/serveur/groupe_perms?del_path=%s%s">supprimer</a></td></tr>""" % (ficperso, choice, occurences, ficperso, gr_string)
                self.content += """<tr><td colspan="4" align="center"><a href="/serveur/groupe_perms?del_path=droits_zephir%s">Tout supprimer</a></td></tr></table>""" % (gr_string)
            else:
                self.content += """<p><font size=+1>Pas de permissions spécifiées pour ce serveur</font></p>"""
        except Exception as e:
            self.content = e

        return self.content


class GroupeServeur(Design):
    """critères de sélection d'un groupe de serveurs : module
    """

    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'groupe':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Sélection des serveurs"

    def wmfactory_content(self, request):
        return self.content

    def _dump_html(self, options):
        """affiche la page de recherche
        """
        return """
<h1>Sélectionner un groupe de serveur</h1>
<h2>Choisir une version de module ou cliquer sur suivant</h2>
<form method="post" action="groupe_crit">
<table cellpadding="5" cellspacing="0" align="center">
<tr><td>module</td><td>%s</td></tr>
<tr><td><input type=submit value="Suivant"></td>
<td><input name="initialiser" value="Réinitialiser" type="reset"/></td></tr>
<tr><td colspan="2"> <br/>
<a href="/serveur">Retour à la gestion des serveurs</a></td></tr>
</table></form><br/>
        """ % options
        #align="center

    def _dump_modules(self,request):
        """créé une liste des modules"""
        modules = backend(proxy(request).modules.get_module())
        l = ['<select name="module">','<option value=""></option>']
        l.append(pretty_select_module(modules))
        l.append("</select>")
        return "\n".join(l)

    def renderView(self, request):
        try:
            try:
                self.content = self._dump_html(self._dump_modules(request))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                traceback.print_exc()
                raise BackendError()

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class RemoveGroupe(Design):
    """supprime le groupe stocké dans la session
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'remove_groupe':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Désélection du groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        prefs = request.getSession()
        if hasattr(prefs, 'groupe_serveur'):
            delattr(prefs, 'groupe_serveur')
        if hasattr(prefs, 'groupe_alerte'):
            delattr(prefs, 'groupe_alerte')
        self.content = """<p><span id="message">Groupe désélectionné</span></p><p><a href="/serveur">Retour à la page de gestion des serveurs</a></p>"""
        return self.content

class GroupeCriteres(Design):
    """sélection d'un groupe : choix des critères
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'groupe_crit':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Critères de sélection"

    def wmfactory_content(self, request):
        return self.content

    def _options_types_etab(self,request):
        # récupération des types d'établissement existants
        dico = backend(proxy(request).etabs.get_types())[0]
        l = []
        dico_inv = {}
        for clef, valeur in list(dico.items()):
            dico_inv[valeur] = clef
        liste = list(dico_inv.keys())
        liste.sort()
        max_len = 0
        for clef in liste:
            s = """<OPTION VALUE="%s">%s</OPTION>""" % (dico_inv[clef],clef)
            if len(clef) > max_len:
                max_len = len(clef)
            l.append(s)
        l.insert(0, """<OPTION VALUE="">%s</OPTION>""" % ("-"*max_len))
        return "\n".join(l)

    def _dump_variantes(self,module,request):
        """créé une liste des variantes"""
        variantes = self.get_variante_module(int(module),proxy(request))
        l = ['<select name="variante">','<OPTION VALUE=""></OPTION>']
        for var in list(variantes.keys()) :
            s = """<OPTION VALUE="%s">%s</OPTION>""" % (var, variantes[var])
            l.append(s)
        l.append("</select>")
        return "\n".join(l)

    def _dump_html(self,module,libelle,liste,request):
        """Affichage de la liste des critères
        """
        # on récupère les libellés des modules et variantes
        variantes_lib = self.get_variante(proxy(request))
        etats = [('', "---------------------------------------------"),
                 ('1',"Pas d'erreur"),
                 ('', "---------------------------------------------"),
                 ('alertes', "tout type d'erreur"),
                 ('2',"Contact perdu"),
                 ('0',"Erreur dans les données de surveillance"),
                 ('3',"Commandes bloquées"),
                 ('4',"Blocage + surveillance"),
                 ('', "---------------------------------------------"),
                 ('null',"État non défini (nouveau serveur)")]
        etat_maj = [('', "-----------------------------"),
                    ('uptodate',"Serveur à jour"),
                    ('outdated',"Serveur non à jour"),
                    ('unknown',"Pas d'information")]
        etat_conf = [('', "-----------------------------"),
                    ('1',"Non modifiés"),
                    ('0',"Modifiés sur le serveur"),
                    ('null',"Pas d'information")]
        etat_alerte = [('', 'Indifférent'),
                       ('0', 'Alertes autorisées'),
                       ('1', 'Alerte bloquées')]
        l = []
        l.append("""<h1>Sélectionner un groupe de serveur</h1><h2>Entrer un ou plusieurs critères de recherche ou cliquer sur suivant</h2>
        <br/>%s<br/><br/>""" % config.ilike_syntax)
        l.append("""<script LANGUAGE="JavaScript"><!--
        function validfields(form)
        {
            //getting the values:
            if (form.rne.value.length > 8)
            {
                alert("RNE : 8 caractères maximum !");
                form.rne.focus();
                return false;
            }
            else
            {
                return true;
            }
        }
        function switch_type_et()
        {
            select_type = document.forms['form_crit']['type_etab'];
            select_lib = document.forms['form_crit']['type_etab_lib'];
            if (select_type.value != "")
            {
                select_lib.disabled = "disabled";
            }
            else
            {
                select_lib.disabled = "";
            }
        }
        //--></script>
        <FORM METHOD="POST" ACTION="groupe_vars" name="form_crit" onSubmit="return validfields(this)">
        <table cellpadding="5" cellspacing="0" align="center" valign="middle" width="80%%">""")
        if module != "":
            l.append("""<tr><td>Variante (%s)</td><td>%s</td></tr>""" % (libelle, liste))
        else:
            l.append("""<input type="hidden" size="8" name="variante" value=""/>""")
        l.append("""<tr><td>RNE</td><td><input type="text" size="8" name="rne" value=""/></td></tr>
        <input type="hidden" size="8" name="module_actuel" value="%s"/>
        <tr><td>Libellé</td><td><input type="text" size="50" name="libelle" value=""/></td></tr>
        <tr><td>Matériel</td><td><input type="text" size="50" name="materiel" value=""/></td></tr>
        <tr><td>Processeur</td><td><input type="text" size="30" name="processeur" value=""/></td></tr>
        <tr><td>Disque dur</td><td><input type="text" size="30" name="disque_dur" value=""/></td></tr>
        <tr><td>Installateur</td><td><input type="text" size="30" name="installateur" value=""/></td></tr>
        <tr><td>Remarques</td><td><input type="text" size="50" name="remarques" value=""/></td></tr>
        <tr><td>Téléphone</td><td><input type="text" size="10" name="tel" value=""/></td></tr>
        <tr><td>Type d'établissement</td>
        <td><select name="type_etab" onChange="javascript:switch_type_et()"/>
        %s
        </select><input type="text" value="" name="type_etab_lib"/></td></tr>
        <tr><td>Date d'installation</td><td><select name="date_operator">
        <option value="&lt;">avant le</option>
        <option value="=" selected="selected">le</option>
        <option value="&gt;">après le</option></select>
        <input type="text" size="2" name="jour" value=""/>&nbsp;<input type="text" size="2" name="mois" value=""/>&nbsp;<input type="text" size="4" name="annee" value=""/></td></tr>
        <tr><td>État du serveur</td><td><select name="etat">""" % (module, self._options_types_etab(request)))
        for data in etats:
            l.append("""<option value="%s">%s</option>""" % data)
        l.append("""</select></tr></td>
        <tr><td>Paquets à mettre à jour</td><td><select name="maj">""")
        for data in etat_maj:
            l.append("""<option value="%s">%s</option>""" % data)
        l.append("""</select></tr></td>
        <tr><td>Patches et configuration</td><td><select name="md5s">""")
        for data in etat_conf:
            l.append("""<option value="%s">%s</option>""" % data)
        l.append("""</select></tr></td>
        <tr><td>Blocage des alertes</td><td><select name="no_alert">""")
        for data in etat_alerte:
            l.append("""<option value="%s">%s</option>""" % data)
        l.append("""</select></tr></td>
        <tr><td colspan="2" align="left"><input type="checkbox" name="no_contact"/>Serveurs non enregistrés</td></tr>
        <tr><td colspan="2" align="left"><input type="checkbox" name="dictpaqs_detected"/>Serveurs sur lesquels une installation manuelle de dictionnaires est détectée</td></tr>
        <tr><td colspan="2" align="center"><input type="submit" value="Suivant"/> <input name="initialiser" value="Réinitialiser" type="reset"/></td></tr>
        </table>
        </form>""")
        l.append("""<p><a href="javascript:history.back()">Retour à la sélection d'un groupe de module</a><br/></p>""")
        return "".join(l)

    def renderView(self, request):
        try:
            try:
                modules_lib = self.get_module(proxy(request))
                module = escape(request.args[b'module'][0])
                if module != "" :
                    libelle_module = modules_lib[int(module)][0]
                    liste = self._dump_variantes(int(module),request)
                else:
                    module = ""
                    liste = []
                    libelle_module = ""
                self.content = self._dump_html(module,libelle_module,liste,request)
            except Exception as e:
                raise FrontendError(str(e))

        except xmlrpclib.ProtocolError:
            self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class GroupeVariables(Design):
    """sélection d'un groupe : choix de valeurs de variables creole
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'groupe_vars':
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Sélection par valeurs de configuration"

    def wmfactory_content(self, request):
        return self.content

    def get_liste_vars(self, dico, request):
        """variables creole disponibles
        """
        recup_dico = None
        if 'variante' in dico and dico['variante'] != '':
            recup_dico = backend(proxy(request).modules.get_dico(int(dico['module_actuel']),int(dico['variante'])))
        elif 'module_actuel' in dico and dico['module_actuel'] != '':
            recup_dico = backend(proxy(request).modules.get_dico(int(dico['module_actuel'])))
        # liste
        if recup_dico:
            # on détermine la version de creole (2 ou 3)
            mod_infos = backend(proxy(request).modules.get_module(int(dico['module_actuel'])))
            creole_version = config.CREOLE_VERSIONS[mod_infos[0]['version']]
            dico_eole = ZephirDict(mode='', version=creole_version)
            dico_eole.init_from_zephir(recup_dico)
            liste = list(dico_eole.liste_vars.keys())
            liste.sort()
            return liste
        else:
            return []

    def _dump_html(self, groupe_vars, dico, liste_vars):
        """critères de sélection par variables creole
        """
        l = ["""<h1>Sélectionner un groupe de serveur</h1><h2>Filtrer la recherche avec une ou plusieurs valeurs de configuration</h2>"""]
        index_max = len(groupe_vars) + 1
        l.append("""<script language='javascript'>
        function copy_selection(liste_name,input_name) {
        inputbox = document.forms['form_vars'][input_name];
        liste = document.forms['form_vars'][liste_name];
        val = liste.options[liste.selectedIndex].value;
        if (val != '') {
            inputbox.disabled="disabled";
            inputbox.value = val;
            }
        else {
            inputbox.disabled="";
            inputbox.value = "";
        }
        };
        function get_varname() {
        inputbox = document.forms['form_vars']['input_varname'];
        varbox = document.forms['form_vars']['var_%d'];
        varbox.value = inputbox.value;
        }
        </script>""" % index_max)

        l.append("""<FORM NAME="form_vars" METHOD="POST" ACTION="groupe_vars" onSubmit="javascript:get_varname()">""")
        for crit in dico:
            l.append("""<input type="hidden" name="%s" value="%s">""" % (crit, dico[crit]))
        l.append("""<table cellpadding="2" cellspacing="0" align="center" valign="middle">
        <tr><th>Nom de variable</th><th></th><th>Valeur</th>""")
        var_index = 1
        for var in groupe_vars:
            if groupe_vars[var][2] == True:
                aff_negate = """différent de<input type="hidden" name="negate_var_%d" value="1">""" % var_index
            else:
                aff_negate = """égal à<input type="hidden" name="negate_var_%d" value="0">""" % var_index

            l.append("""<tr><td>%s<input type="hidden" name="var_%d" value="%s"/></td>
                        <td>%s</td>
                        <td>%s<input type="hidden" name="val_var_%d" value="%s"/></td></tr>
                        """ % (groupe_vars[var][0], var_index, groupe_vars[var][0], aff_negate, groupe_vars[var][1], var_index, groupe_vars[var][1]))
            var_index += 1

        # si liste de variables -> liste déroulante
        select_var = ""
        if liste_vars != []:
            select_var = """<select name="variables_%d" onChange="javascript:copy_selection('variables_%d','input_varname')">
            <option selected="selected" value="" align="center">- autre -</option>""" % (var_index, var_index)
            for varname in liste_vars:
                select_var += """<option value="%s">%s</option>""" % (str(varname), str(varname))
            select_var += """</select>"""
        # saisie pour nouvelle variable
        l.append("""<tr><td>%s<input type="text" name="input_varname" value=""/><input type="hidden" name="var_%d" value=""/></td>
                    <td><select name="negate_var_%d">
                    <option selected="selected" value="0">égal à</option><option value="1">différent de</option>
                    </select></td><td><input type="text" name="val_var_%d" value=""/></td></tr>
                    """ % (select_var, var_index, var_index, var_index))
        l.append("""<tr><td colspan="3" align="center"><p><input type="submit" value="Valider les conditions">""")
        l.append("""<input name="initialiser" value="Réinitialiser" type="reset"/></p></td></table></form><br/>""")
        # validation du formulaire avec tous les critères
        l.append("""<FORM ACTION="aff_groupe" METHOD="POST">""")
        if len(groupe_vars) > 1:
            l.append("""Les serveurs doivent remplir <select name="strict_mode">
            <option selected="selected" value="1">Toutes les conditions ci-dessus</option>
            <option value="0">Au moins une des conditions ci-dessus</option>
            </select>""")
        else:
            l.append("""<input type="hidden" name="strict_mode" value="1">""")
        # critères divers
        for crit in dico:
            l.append("""<input type="hidden" name="%s" value="%s">""" % (crit, dico[crit]))
        # critères des variables creole
        var_index = 1
        for var in groupe_vars:
            l.append("""<input type="hidden" name="var_%d" value="%s"/>""" % (var_index, groupe_vars[var][0]))
            if groupe_vars[var][2] == True:
                l.append("""<input type="hidden" name="negate_var_%d" value="1">""" % var_index)
            else:
                l.append("""<input type="hidden" name="negate_var_%d" value="0">""" % var_index)
            l.append("""<input type="hidden" name="val_var_%d" value="%s"/>""" % (var_index, groupe_vars[var][1]))
            var_index += 1
        l.append("""<input type="submit" value="Suivant"/>""")
        l.append("</form>")
        l.append("""<p><a href="javascript:history.back()">Retour vers les critères de recherche</a><br/></p>""")

        return "".join(l)

    def renderView(self, request):
        try:
            # on regarde si des critères de sélection existent
            dico = {}
            liste2=None
            criteres = ['rne', 'module_actuel', 'variante', 'installateur', 'materiel', 'libelle', 'remarques', 'etat', 'processeur', 'disque_dur', 'tel','maj','md5s','annee','mois','jour','date_operator', 'no_contact','no_alert','type_etab', 'type_etab_lib', 'dictpaqs_detected']
            for critere in criteres:
                if critere.encode() in request.args:
                    dico[critere] = escape(request.args[critere.encode()][0])
            # si module ou variante choisis, on récupère la liste de variables disponibles
            liste_vars = self.get_liste_vars(dico, request)
            # si on ne donne pas de critères, on regarde
            # si une sélection existe déjà dans la session utilisateur
            groupe_vars = get_groupe_vars(request)
            # stockage des critères
            try:
                self.content = self._dump_html(groupe_vars, dico, liste_vars)
            except Exception as e:
                raise FrontendError(str(e))

        except xmlrpclib.ProtocolError:
            self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content


class AffGroupe(Design):
    """résultat de la sélection d'un groupe
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'aff_groupe':
            return self
        return Serveur()

    def wmfactory_menu(self, request):
        return Navigation(aide="howto/page4.html#section2").menu()

    def wmfactory_title(self,request):
        return "Liste des serveurs choisis"

    def wmfactory_content(self, request):
        return self.content

    def _get_sort_key(self, server, sort_key):
        if sort_key == 'module_actuel':
            return self.modules_lib[server[sort_key]][0].lower()
        elif sort_key == 'variante':
            return self.variantes_lib[server[sort_key]].lower()
        elif sort_key in ('libelle', 'rne', 'installateur'):
            return server[sort_key].lower()
        else:
            return server[sort_key]

    def _dump_html(self,liste,serveur_status,id_groupe,request):
        """Affichage de la liste des serveurs choisis
        """
        sort_dict = {}
        for crit in list(config.server_sort_keys.keys()):
            if id_groupe != "":
                sort_dict[crit] = """<a href="/serveur/aff_groupe?id_groupe=%s&sort_key=%s&reverse=1">
                <img border="0" src="/images/gnome_up.png" title="Tri décroissant"></a>
                </td><td><a href="/serveur/aff_groupe?id_groupe=%s&sort_key=%s">
                <img border="0" src="/images/gnome_down.png" title="Tri croissant"></a></td><td class="col">
                """ % (id_groupe, crit, id_groupe, crit)
            else:
                sort_dict[crit] = """<a href="/serveur/aff_groupe?sort_key=%s&reverse=1">
                <img border="0" src="/images/gnome_up.png" title="Tri décroissant"></a>
                </td><td><a href="/serveur/aff_groupe?sort_key=%s">
                <img border="0" src="/images/gnome_down.png" title="Tri croissant"></a></td><td class="col">
                """ % (crit, crit)

        l = []
        l.append("""<table id="tab_serveurs" cellpadding="0" cellspacing="2">
            <td><table><tr><td>%(id)sID</td></tr></table></td><td><table><tr><td>%(libelle)sLibellé</td></tr></table></td>
            <td><table><tr><td>%(rne)sRNE</td></tr></table></td><td><table><tr><td>%(installateur)sInstallateur</td></tr></table></td>
            <td><table><tr><td>%(module_actuel)sModule</td></tr></table></td><td><table><tr><td>%(variante)sVariante</td></tr></table></td>
            <td><table><tr><td>%(nb_cmd)sActions</td></tr></table></td><td class="col">Téléch.</td><td><table><tr><td>%(maj)sMAJ</td></tr></table></td>
            <td><table><tr><td>%(md5s)sMD5</td></tr></table></td><td><table><tr><td>%(dicos)sDicos</td></tr></table></td><td class="col">État</td>
            <td><table><tr><td>%(detail)sDétails</td></tr></table></td></tr>""" % sort_dict)
        for d in liste:
            # infos générales
            l.append("""\n<tr><td align="center">%s</td><td><a href="/serveur/etat?id=%s&id_groupe=%s">""" % (d['id'], d['id'], id_groupe))
            l.append("""%(libelle)s</td><td><a href="/etab/get?rne=%(rne)s">%(rne)s</a></td><td>%(installateur)s</td>""" % d)
            # somme md5 de la configuration (zephir.eol + patches)
            if d['md5s'] == -1:
                md5s = """<img border="0" src="/images/diode_grise.gif" border="0" title="pas d'information">"""
            elif d['md5s'] == 0:
                nb_modifs = len(serveur_status[d['id']]['md5s'][1].split(';'))
                md5s = """<img border="0" src="/images/diode2.gif" border="0" title="configuration non synchronisée : %s fichiers modifiés">""" % str(nb_modifs)
            else:
                md5s = """<img border="0" src="/images/diode_verte2.gif" border="0" title="fichiers synchronisés">"""
            # paquets de dictionnaires
            if d['dicos'] == 0:
                nb_paqs = len(serveur_status[d['id']]['dictpaqs_ok'][1])
                dicos = """<img border="0" src="/images/diode2.gif" border="0" title=" %d paquet(s) non pris en compte">""" % nb_paqs
            elif d['dicos'] == 1:
                dicos = """<img border="0" src="/images/diode_verte2.gif" border="0" title="pas de dictionnaires non activés">"""
            else:
                dicos = """<img border="0" src="/images/diode_grise.gif" border="0" title="pas d'information">"""
            # maj
            try:
                assert int(d['maj']) >= 0
                if d['maj'] == 0:
                    maj = """<img border="0" src="/images/diode_verte2.gif" border="0" title="serveur à jour">"""
                else:
                    maj = """<img border="0" src="/images/diode_orange.gif" border="0" title="%s paquets non à jour">""" % d['maj']
            except:
                maj = """<img border="0" src="/images/diode_grise.gif" border="0" title="pas d'informations">"""
            # téléchargement des paquets d'upgrade
            if d['upgrade'] < 0:
                upgrade = """<img border="0" src="/images/diode_grise.gif" border="0" title="Pas d'information">"""
            elif d['upgrade'] == 0:
                upgrade = """<img border="0" src="/images/diode2.gif" border="0" title="Echec : %s">""" % str(serveur_status[d['id']]['upgrade_ok'][2])
            else:
                upgrade = """<img border="0" src="/images/diode_verte2.gif" border="0" title="%s">""" % str(serveur_status[d['id']]['upgrade_ok'][2])
            # liste des commandes et état
            if d['nb_cmd'] > 0:
                cmds = """<a href="/serveur/uucp?id=%s&id_groupe=%s">%s</a>""" % (d['id'], id_groupe, d['nb_cmd'])
            else:
                cmds = ""
            l.append("""<td>%s</td><td>%s</td><td align=center>%s</td><td align="center">%s</td><td align="center">%s</td><td align="center">%s</td><td align="center">%s</td><td>%s</td><td>%s</td></tr>""" % \
                    (self.modules_lib[int(d['module_actuel'])][0], self.variantes_lib[int(d['variante'])], cmds, upgrade, maj, md5s, dicos, d['etat'],d['detail']))

        l.append("""</table><p>""")

        return "".join(l)

    def renderView(self, request):
        try:
            id_groupe = "-1"
            sort_key = ""
            reverse = False
            strict_mode = True
            if b'sort_key' in request.args:
                sort_key = escape(request.args[b'sort_key'][0])
            if b'reverse' in request.args and escape(request.args[b'reverse'][0]) == "1":
                reverse = True
            try:
                try:
                    id_groupe = escape(request.args[b'id_groupe'][0])
                    assert str(id_groupe) != "-1"
                    # lecture d'un groupe sauvegardé
                    liste_id = backend(proxy(request).serveurs.get_groups(id_groupe))
                    if liste_id == []:
                        raise BackendError("""Vous n'êtes pas autorisé à accéder à ce groupe""")
                    liste2 = backend(proxy(request).serveurs.groupe_reload(liste_id[0][2]))
                except KeyError:
                    raise AssertionError
                except IndexError:
                    raise AssertionError
            except AssertionError:
                try:
                    # on regarde si des critères de sélection existent
                    dico = {}
                    liste2 = None
                    no_criter = True
                    params = {}
                    criteres = ['rne', 'module_actuel', 'variante', 'installateur', 'materiel', 'libelle', 'remarques', 'etat', 'processeur', 'disque_dur', 'tel','maj','md5s','no_alert','type_etab', 'type_etab_lib']
                    for critere in criteres:
                        if critere.encode() in request.args:
                            if escape(request.args[critere.encode()][0]) != "":
                                no_criter = False
                                dico[critere] = escape(request.args[critere.encode()][0])
                    if b'dictpaqs_detected' in request.args:
                        params['dictpaqs_ok'] = 0
                    if b'no_contact' in request.args:
                        dico['last_contact'] = "null"
                        no_criter = False
                    if b'annee' in request.args:
                        date = "%s-%s-%s" % (escape(request.args[b'annee'][0]), escape(request.args[b'mois'][0]), escape(request.args[b'jour'][0]))
                        if date != "--":
                            dico['date_install'] = request.args[b'date_operator'][0].decode() + " '%s'" % date
                            no_criter = False
                    # critères de selection sur la configuration
                    groupe_vars = get_groupe_vars(request)
                    if groupe_vars != {}:
                        no_criter = False
                    if b'strict_mode' in request.args:
                        strict_mode = bool(int(request.args[b'strict_mode'][0].decode()))
                        no_criter = False
                    if params:
                        # champs params, critères spécifiques hors SQL
                        dico['params'] = params
                        no_criter = False
                    assert no_criter == False
                except:
                    try:
                        # si on ne donne pas de critères, on regarde
                        # si une sélection existe déjà dans la session utilisateur
                        prefs = request.getSession()
                        liste2 = prefs.groupe_serveur
                    except:
                        raise FrontendError("critères de sélection")

            try:
                if liste2 is None:
                    # chargement des infos pour la liste d'identifiants demandée
                    liste2 = backend(proxy(request).serveurs.groupe_serveur(u(dico),groupe_vars,strict_mode))
                # récupération des infos sur les serveurs à partir des critères de sélection
                dic_status = {}
                list_id = [serv['id'] for serv in liste2]
                globs = backend(proxy(request).serveurs.global_status(list_id))
                details = backend(proxy(request).serveurs.get_status(list_id))
                ag_stats = backend(proxy(request).serveurs.agents_status(list_id))
                serveur_status = {}
                for id_s, glob, detail in zip(list_id, globs, details):
                    dic_status[id_s] = (glob, detail)
                    serveur_status[id_s] = detail
                liste = []
                # on récupère l'état global des serveurs
                for serveur, ag_serv in zip(liste2, ag_stats):
                    detail_err = []
                    detail_warn = []
                    cmds = 0
                    serveur['detail'] = ""
                    try:
                        # on vérifie l'état zephir
                        status = dic_status[serveur['id']][1]
                        # nombre de commandes en attente
                        if 'uucp_cmd' in status:
                            cmds += status['uucp_cmd']
                        res = 0
                        verifs = ['reconfigure_ok','sauvegarde_ok','timeout','lock_ok','maj_ok','configure_ok','perso_ok']
                        for cle in verifs:
                            if status[cle][0] == 0:
                                # problème lors d'une action sur le serveur
                                detail_err.append(cle.replace('_ok',''))
                                res = 2
                        if status['cle_ok'] == 0:
                            res = 2
                            detail_err.append('non enregistré')
                        if status['agents'] in [0, 2]:
                            # on recherche la liste des agents en erreur
                            agents_err = []
                            agents_warn = []
                            for nom_ag, data_ag in list(ag_serv.items()):
                                if nom_ag != 'tcpservices':
                                    if data_ag[1] == 0:
                                        agents_err.append(data_ag[0])
                                    elif data_ag[1] == 5:
                                        agents_warn.append(data_ag[0])
                            if agents_err:
                                detail_err.extend(agents_err)
                                if res != 2:
                                    res = 3
                            if agents_warn:
                                detail_warn.extend(agents_warn)
                                if res not in [2, 3]:
                                    res = 4
                        if res not in [2, 3, 4]:
                            res = dic_status[serveur['id']][0]
                    except Exception as err:
                        res = -1
                    if res == -1:
                        s = """<img border="0" src="/images/diode_grise.gif" border="0">"""
                        serveur['etat'] = s
                    elif int(res) == 1:
                        # pas d'erreur -> lien sur page d'état
                        s = """<a href="/serveur/etat?id=%s&id_groupe=%s"><img border="0" src="/images/diode_verte2.gif" border="0"></a>""" % (serveur["id"],id_groupe)
                        serveur['etat'] = s
                    elif int(res) == 3:
                        # seulement erreur sur les agents -> lien sur résumé des agents
                        serveur['etat'] = """<a href="https://%s:%s/agents/%s" target="_blank"><img border="0" src="/images/diode2.gif"></a>""" % (str(config.ADRESSE_ZEPHIR),config.PORT_HTTP,serveur['id'])
                    elif int(res) == 4:
                        # seulement erreur sur les agents -> lien sur résumé des agents
                        serveur['etat'] = """<a href="https://%s:%s/agents/%s" target="_blank"><img border="0" src="/images/diode_orange.gif"></a>""" % (str(config.ADRESSE_ZEPHIR),config.PORT_HTTP,serveur['id'])
                    else:
                        # erreur d'état zephir -> lien sur page d'état
                        s = """<a href="/serveur/etat?id=%s&id_groupe=%s"><img border="0" src="/images/diode2.gif" border="0"></a>""" % (serveur["id"],id_groupe)
                        # on essaye de détecter la cause de l'erreur
                        serveur['etat'] = s
                    detail = ""
                    options = ""
                    if detail_err:
                        options_err = "<optgroup label='Erreurs Agents'>{}</optgroup>"
                        options += options_err.format(''.join([f"<option>{opt}</option>" for opt in detail_err]))
                    if detail_warn:
                        options_warn = "<optgroup label='Avertissements Agents'>{}</optgroup>"
                        options += options_warn.format(''.join([f"<option>{opt}</option>" for opt in detail_warn]))
                    if options:
                        detail += f"<select>{options}</select>"
                    serveur['detail'] += detail
                    serveur['nb_cmd'] = cmds
                    # paquets non à jour
                    try:
                        if int(status['query_maj'][0]) >= 0:
                            serveur['maj'] = int(status['query_maj'][0])
                        else:
                            raise ValueError('etat inconnu')
                    except:
                        serveur['maj'] = -1
                    # téléchargement des paquets d'upgrade
                    try:
                        serveur['upgrade'] = int(status['upgrade_ok'][0])
                    except:
                        serveur['upgrade'] = -1
                    # md5 de la configuration
                    try:
                        serveur['md5s'] = int(status['md5s'][0])
                    except:
                        serveur['md5s'] = -1
                    # dictionnaires non pris en compte
                    try:
                        serveur['dicos'] = int(status['dictpaqs_ok'][0])
                    except:
                        serveur['dicos'] = -1
                    liste.append(serveur)

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")

            if liste:
                # on récupère les libellés des modules et variantes
                self.variantes_lib = self.get_variante(proxy(request))
                self.modules_lib = self.get_module(proxy(request))
                # tri des serveurs suivant différents critères
                if liste != []:
                    try:
                        if sort_key != "" and sort_key in list(liste[0].keys()):
                            liste.sort(key=lambda x:self._get_sort_key(x, sort_key),reverse=reverse)
                    except:
                        traceback.print_exc()

                    # on stocke la sélection dans la session
                    prefs = request.getSession()
                    prefs.groupe_serveur = liste
                    if hasattr(prefs, 'groupe_alerte'):
                        delattr(prefs, 'groupe_alerte')


                # récupération de l'état global des agents
                self.content = """<h1>Liste des serveurs sélectionnés</h1><br>"""
                self.content += self._dump_html(liste,serveur_status,id_groupe,request)
                self.content += """<a href="/serveur/action?id_groupe=%s">Actions sur le groupe de serveurs</a><br>""" % id_groupe
            else:
                self.content = """<p><span id="message">Aucun serveur ne correspond à vos critères de recherche</span></p><br/>"""
                self.content += """<p><a href="/serveur/groupe">Retour à la sélection d'un groupe de module</a></p>"""

            if liste:
                self.content += """<script language="JavaScript"><!--
                    function get_login()
                    {
                        //getting the values:
                        var login_auth = document.allow_form.authorize.value;
                        //setting the values:
                        document.deny_form.prohibit.value = login_auth;
                    }
                    //--></script>
                    <h2>Connexion par clé SSH</h2>
                    <p><table cellspacing="0" cellpadding="2">
                    <tr><td rowspan="2">
                    <form name="allow_form" METHOD="POST" action="/serveur/save_groupe">
                    <input type="text" size="20" name="authorize" value="(login utilisateur)"/></td>
                    <td><input type="submit" value="Autoriser">
                    </form></td></tr>
                    <tr><td>
                    <form name="deny_form" METHOD="POST" action="/serveur/save_groupe" onSubmit="javascript:get_login()">
                    <input type="hidden" name="prohibit" value=""/>
                    <input type="submit" value="Interdire">
                    </form></td></tr></table></p>"""

            if liste and str(id_groupe) == "-1":
                self.content += """
                <script language="JavaScript"><!--
                function validfields(form)
                {
                    //getting the values:
                    if (form.libelle.value.length > 50)
                    {
                        alert("libelle : 50 caractères maximum !");
                        form.libelle.focus();
                        return false;
                    } else {
                        return true;
                    }
                }
                function check_group()
                {
                    liste = document.forms['group_add']['extend_gr'];
                    but_gr = document.forms['group_add']['gr_sub'];
                    if (liste.options[liste.selectedIndex].value != "")
                    {
                        but_gr.disabled = "";
                    } else {
                        but_gr.disabled = "disabled";
                    }
                }
                //--></script>
                <h2>Enregistrer la sélection</h2>
                <table><tr><td>
                <form method="post" action="/serveur/save_groupe" onSubmit="return validfields(this)">
                <input type="text" size="50" name="libelle" value="Entrer un libellé pour ce groupe"/></td><td>
                <input type="submit" name="gr_sub" value="Enregistrer ce groupe"></form></td></tr>"""

                # liste des groupes existants
                existing_gr = backend(proxy(request).serveurs.get_groups())
                existing_gr.sort(key=lambda x:x[1].lower())
                if existing_gr != []:
                    self.content += """<tr><td align="right"><form METHOD="POST" name="group_add" action="/serveur/extend_groupe">"""
                    self.content += """<select name="extend_gr" onChange="javascript:check_group()">"""
                    self.content += """    <OPTION VALUE="" selected="selected"></OPTION>"""
                    for gr in existing_gr:
                        self.content += """    <OPTION VALUE="%s">%s</OPTION>""" % (gr[0],gr[1])
                    self.content += """</select></td><td><input type="submit" name="gr_sub" disabled="disabled" value="Ajouter à ce groupe"></form></td></tr>"""
                self.content += """</table><p><a href="/serveur/groupe">Sélectionner un nouveau groupe de serveurs</a><br /><a href="/serveur">Retour à la page de gestion des serveurs</a></p>"""

        except xmlrpclib.ProtocolError:
            self.content = ("""<span id="message">Vous n'êtes pas autorisé à effectuer cette action</span>""")
        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class Agents(Design):
    """Listing des agents en cas de pb sur un serveur
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b'agents':
            return self
        return Serveur()

    def wmfactory_menu(self, request):
        return Navigation(aide="page5.html#section6").menu()

    def wmfactory_title(self,request):
        return "Liste des agents en alerte"

    def wmfactory_content(self, request):
        return self.content


    def _filtre(self, d):
        """Filtre les agents qui ont un pb
        """
        l = []
        for agent in list(d.keys()):
            if int(d[agent][1]) == 0 :
                l.append([agent,d[agent][0], d[agent][2]])
            else:
                pass

        return l

    def _dump_html(self, statuts):
        """Affichage de la liste des serveurs choisis
        """
        liste = self._filtre(statuts)
        l = []
        l.append("""
            <table cellpadding="1" cellspacing="2" border="1">
            <td>agent</td><td>libelle</td><td>date</td></tr>
        """)
        for list in liste:
            l.append("""
            <tr><td>%s</td><td>%s</td><td>%s</td></tr>
            """ % (list[0],list[1],list[2]))

        l.append("""</table><p>""")

        return "".join(l)

    def renderView(self, request):
        try:
            try:
                self.id_serveur = escape(request.args[b'id'][0])
                assert not self.id_serveur == ""
            except Exception:
                raise FrontendError("criteres de selection")
            try:
                etat_agents = backend(proxy(request).serveurs.agents_status(self.id_serveur))

            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception:
                raise BackendError()

            # récupération de l'état global des agents
            if etat_agents == {}:
                self.content = """<h1>Pas de statistique disponible</h1>"""
            else:
                self.content = """<h1>Liste des agents ayant remonté un problème :</h1><br>"""
                self.content +=  self._dump_html(etat_agents)
                self.content += """<a href="javascript:void(window.open('https://'+window.location.hostname+':%s/agents/%s','site', 'resizable=yes, location=no, directories=no, status=no, scrollbars=yes'))">surveillance du serveur</a><br>""" % (config.PORT_HTTP, self.id_serveur)
            self.content += """<a href="javascript:history.back()">Retour</a><br/></p>"""

        except (FrontendError,BackendError) as e:
            self.content = e

        return self.content

class ExtendGroupe(Design):
    """extension d'un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"extend_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Extension du groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            back_link = ""
            try:
                if b'id_serveur' in request.args:
                    back_link = """<p><a href="/serveur/etat?id=%s">Page d'état du serveur</a></p>\n""" % request.args[b'id_serveur'][0].decode()
                    groupe = [int(request.args[b'id_serveur'][0])]
                else:
                    groupe_request=request.getSession().groupe_serveur
                    groupe = [ i['id'] for i in groupe_request ]
                # récupération du n° de groupe à étendre
                groupe_ori = escape(request.args[b'extend_gr'][0])
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except :
                raise FrontendError("groupe de serveurs")
            try:
                result=backend(proxy(request).serveurs.groupe_extend(int(groupe_ori),u(groupe)))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except:
                raise BackendError("erreur lors de l'ajout au groupe")

            self.content = """<p><span id="message">Modifications enregistrées</span></p><br>
            %s<p><a href="/serveur/user_groupe">Retour à la page de gestion des groupes enregistrés</a></p>
            <p><a href="/serveur/aff_groupe?id_groupe=%s">Voir le groupe modifié</a></p>
            """ % (back_link, groupe_ori)

        except Exception as e:
            self.content = e

        return self.content

class SaveGroupe(Design):
    """sauvegarde d'un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"save_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Enregistrement du groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        # ok permet de savoir si on a effectué une action ou pas
        ok=0
        try:
            try:
                groupe_request=request.getSession().groupe_serveur
                groupe = [ i['id'] for i in groupe_request ]
            except:
                raise FrontendError("groupe de serveurs")
            try:
                # autorisations
                user_name = escape(request.args[b"authorize"][0])
                backend(proxy(request).serveurs.authorize_user(u(user_name), u(groupe)))
                ok=1
            except KeyError:
                pass
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            if not ok:
                try:
                    # interdictions
                    user_name = escape(request.args[b"prohibit"][0])
                    backend(proxy(request).serveurs.deny_user(u(user_name), u(groupe)))
                    ok=1
                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except KeyError:
                    pass
                except :
                    raise BackendError()
            if not ok:
                try:
                    # on récupère le groupe dans la session
                    libelle = escape(request.args[b'libelle'][0])
                except Exception:
                    raise FrontendError("libelle")
                try:
                    result=backend(proxy(request).serveurs.save_group(u(libelle),u(groupe)))
                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except:
                    raise BackendError("erreur de sauvegarde du groupe")

            self.content = """<p><span id="message">Modifications enregistrées</span></p>
            <p><a href="/serveur/user_groupe">Aller à la liste des groupes enregistrés</a></p>
            """

        except Exception as e:
            self.content = e

        return self.content

class UserGroupe(Design):
    """Gère les groupes de serveurs existants
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"user_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Gestion des groupes de serveurs"

    def wmfactory_content(self, request):
        return self.content

    def sort_fonctions(self,a,b):
        if a[0]==b[0]:
            return 0
        elif a[0] > b[0]:
            return 1
        else:
            return -1

    def formulaire(self, groups,user):
        """ affiche les groupes de serveurs existants et ceux surveillés par l'utilisateur
        """

        # conversion de la liste des groupes de l'utilisateur
        user_groups = user[8]

        if not user:
            return """<p>Vous devez saisir un nom d'utilisateur.<br>
        <a href="javascript:history.back()">Retour</a></p>"""
        else:
            l = ["""<FORM METHOD='POST' ACTION='/serveur/mod_usergroups'><center><h1>Groupes enregistrés disponibles</h1><table cellpadding='15' cellspacing='0' align='center' valign='middle' width='100%'><!--contenu2--><tr><td align=center>"""]
            string = """<input type="hidden" name="user" value="%s">""" % (user[0])
            l.append(string)
            l.append("""<table id="tab_groupes_dispo"><tr><td class="col">Identifiant</td><td class="col">Nom du groupe</td><td class="col">Serveurs</td><td class="col">Surveiller</td><td></td>""")
            # tri de la liste
            groups.sort(key=lambda x:x[1].lower())
            for groupe in groups :
                if groupe[0] in user_groups:
                    coche = " checked "
                else:
                    coche = ""
                string = """<tr><td align="center">%s</td><td><a href="/serveur/aff_groupe?id_groupe=%s">%s</a></td><td align="center">%d</td><td align="center"><input type="checkbox" name="%s" %s></td><td><a href="/serveur/detail_groupe?id=%s">(éditer)</a></td></tr>""" % (str(groupe[0]), str(groupe[0]), groupe[1], len(groupe[2]), str(groupe[0]), coche, str(groupe[0]))
                l.append(string)

            l.append("""</td></tr></table>
            </td></tr><tr><td><center><INPUT type='SUBMIT' value='Modifier'></center></FORM></td></tr>""")
            l.append("""</table><!--fin:contenu2-->""")

            return "".join(l)

    def renderView(self, request):
        # session dans self.prefs
        self.prefs=request.getSession()
        # on récupère l'utilisateur courant
        user = get_user(request)
        # calcul de la page
        try:
            groups = backend(proxy(request).serveurs.get_groups())
            info_user = backend(proxy(request).get_user(u(user)))
            try:
                if groups == []:
                    self.content = """<p><span id="message">Pas de groupe enregistré</span></p><p><a href="/serveur/groupe">Sélectionner / enregistrer un groupe de serveurs</a></p><p><a href="/serveur">Retour à la page de gestion des serveurs</a></p>"""
                else:
                    self.content = self.formulaire(groups,info_user)
            except xmlrpclib.ProtocolError:
                self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
            except Exception as e:
                self.content = "erreur frontend : %s" % str(e)
        except Exception as e:
            self.content = e
        return self.content


class ModUserGroups(Design):
    """Prise en compte des modifications des groupes de
    serveurs de l'utilisateur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"mod_usergroups" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Modification des groupes surveillés"

    def wmfactory_content(self, request):
        # tentative de modification des autorisations
        #autorisations = proxy(request).get_user(self.user)
        new_groups = [ int(i) for i in self.modifs ]
        try:
            proxy(request).user_group(self.user,new_groups)
        except xmlrpclib.ProtocolError:
            return """<br>Vous n'avez pas la permission d'effectuer cette action !<br><br>"""
        except Exception:
            return "erreur backend"
        else:
            l=["<p><span id=\"message\">La liste des groupes surveillés par %s a été mise à jour</span></p>" % self.user]
            l.append("""<p><a href="/serveur/user_groupe">Retour à la liste des groupes enregistrés</a></p>""" )
            return "".join(l)

    def renderView(self, request):
        self.modifs = [arg.decode() for arg in request.args if arg != b'user']
        self.user=escape(request.args[b'user'][0])
        return self.content

class DetailGroupe(Design):
    """Affichage du détail d'un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"detail_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Édition du groupe"

    def wmfactory_content(self, request):
        return self.content

    def formulaire(self, groupe, serveurs, modules, selection):
        """création du contenu de la page
        """
        # mise en forme des infos serveurs et modules
        libelle_mod={}
        for module in modules:
            libelle_mod[module['id']]=module['libelle']
        detail_serveur={}
        for serveur in serveurs:
            try:
                detail_serveur[serveur['id']]=[serveur['libelle'],serveur['rne'],serveur['module_actuel']]
            except KeyError:
                # serveur inexistant dans la base
                pass
        liste_serveurs=[]
        for serveur in groupe[2]:
            if serveur in detail_serveur:
                # récupération infos utiles (id, libelle, rne, module) + tri par etablissement, module
                liste_serveurs.append((serveur,detail_serveur[serveur][0],detail_serveur[serveur][1],libelle_mod[detail_serveur[serveur][2]]))
        liste_serveurs.sort(key=lambda x:x[2])
        liste_serveurs.sort(key=lambda x:x[3])
        l=["""<h1>Édition du groupe %s</h1><p>
        <script LANGUAGE="JavaScript"><!--
        function validfields(form)
        {
            //getting the values:
            if (form.libelle.value.length > 50)
            {
                alert("libelle : 50 caractères maximum !");
                form.libelle.focus();
                return false;
            }
            else
            {
                return true;
            }
        }
        function toggle_delete()
        {
            document.forms['form_edit'].type_action.value="delete"
        }
        function toggle_add()
        {
            document.forms['form_edit'].type_action.value="new"
        }
        function allow_new(text_input)
        {
            if (text_input.value != "")
            {
                document.forms['form_edit'].sub_add.disabled = "";
                document.forms['form_edit'].sub_add.focus();
            }
            else
            {
                document.forms['form_edit'].sub_add.disabled = "disabled";
            }
        }
        function CheckSel()
        {
            nb_sel = 0;
            count = document.forms['form_edit'].elements.length;
            for (i=0; i < count; i++) {
                if (document.forms['form_edit'].elements[i].type=="checkbox") {
                    if (document.forms['form_edit'].elements[i].checked==1) {
                        nb_sel += 1;
                    }
                }
            }
            if (nb_sel == 0) {
                alert('Vous devez sélectionner au moins un serveur');
                return false;
            }
            else {
                return true;
            }
        }
        function CheckAll()
        {
           count = document.forms['form_edit'].elements.length;
           for (i=0; i < count; i++) {
              if (document.forms['form_edit'].elements[i].type=="checkbox") {
                  document.forms['form_edit'].elements[i].checked = 1;
              }
           }
        }
        function UnCheckAll()
        {
           count = document.forms['form_edit'].elements.length;
           for (i=0; i < count; i++) {
               if (document.forms['form_edit'].elements[i].type=="checkbox") {
                   document.forms['form_edit'].elements[i].checked = 0;
              }
           }
        }
        //--></script>
        <FORM METHOD="POST" ACTION="/serveur/detail_groupe"  onSubmit="return validfields(this)">
        <input type="hidden" name="id" value="%s">
        <input type="text" size="50" name="libelle" value="%s">
        <INPUT type=SUBMIT value="Modifier le libellé">
        </FORM></p><br/>""" % (groupe[1],str(groupe[0]),groupe[1])]
        l.append("""<FORM METHOD="POST" ACTION="/serveur/detail_groupe" name="form_edit" onSubmit="return CheckSel()">
        <input type="hidden" name="type_action" value=""><table border="1">
        <tr><td colspan="5" align="center">Serveurs sélectionnés <input type="button" value="Tous" onclick="CheckAll()">
        <input type="button" value="Aucun" onclick="UnCheckAll()"></td>""")
        # actions sur la sélection
        l.append("""<tr><td colspan="2" align="center"><b>Édition</b></td><td colspan="3">""")
        # création d'un nouveau groupe depuis la sélection
        l.append("""<table><tr><td>Créer un nouveau groupe</td><td><input type="text" name="new_group_name" value="" onChange="allow_new(this)"><input type="submit" onClick="toggle_add()" disabled="disabled" name="sub_add" value="Créer ce groupe"/></td></tr>""")
        # suppression des serveurs sélectionnés du groupe actuel
        l.append("""<tr><td><font color="red">Suppression</font></td><td><input type="hidden" name="id" value="%s"/><input type="submit" onClick="toggle_delete()" value="Enlever les serveurs sélectionnés du groupe"/></td></tr></table></td></tr>""" % groupe[0])
        l.append("""<tr><td></td><td class="col" align="center">Identifiant</td><td class="col" align="center">Libellé</td><td class="col" align="center">Établissement</td><td class="col" align="center">Type de serveur</td></tr>""")
        for serveur in liste_serveurs:
            if serveur[0] in selection:
                checked = " checked"
            else:
                checked = ""
            try:
                l.append("""<tr><td><input type="checkbox"%s name="serv_%s"/></td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>""" % \
                        (checked, serveur[0], serveur[0], serveur[1], serveur[2], serveur[3]))
            except:
                l.append("""<tr><td><input type="checkbox"%s name="serv_%s"/></td><td>%s</td><td colspan="3">Serveur introuvable dans la base</td></tr>""" % (checked, serveur[0], serveur[0]))
        l.append("""</table></FORM>""")

        return "".join(l)

    def renderView(self, request):
        try:
            try:
                # on récupère le groupe passé en parametre
                id_groupe = escape(request.args[b'id'][0])
            except Exception:
                raise FrontendError("id du groupe")
            try:
                # on récupère les données sur zephir
                groupe=backend(proxy(request).serveurs.get_groups(int(id_groupe)))[0]
                serveurs=backend(proxy(request).serveurs.get_serveur())
                modules=backend(proxy(request).modules.get_module())
            except Exception as e:
                raise BackendError("erreur de sauvegarde du groupe")
            # on regarde si une supression de serveur est demandée
            selection = []
            try:
                type_action = request.args.get(('type_action').encode(), [b""])[0].decode()
                if type_action == "new":
                    for param in request.args.keys():
                        param = param.decode()
                        if param.startswith('serv_'):
                            selection.append(int(param[5:]))
                    new_group_name = request.args.get('new_group_name',[''])[0]
                    if new_group_name == '':
                        raise FrontendError('Nom du nouveau groupe invalide')
                    modifs=1
                    backend(proxy(request).serveurs.save_group(u(new_group_name), selection))
                elif type_action == "delete":
                    for param in request.args:
                        param = param.decode()
                        if param.startswith('serv_'):
                            groupe[2].remove(int(param[5:]))
                    backend(proxy(request).serveurs.edit_group(u(groupe[0]),u(groupe[1]),u(groupe[2])))
            except KeyError:
                traceback.print_exc()
            except:
                traceback.print_exc()
                raise BackendError("erreur lors de la modification")
            # on regarde si le libellé doit être modifié
            try:
                new_libelle = escape(request.args[b'libelle'][0])
                modifs=1
                groupe[1]=new_libelle
                backend(proxy(request).serveurs.edit_group(u(groupe[0]),u(groupe[1]),u(groupe[2])))
            except KeyError:
                pass
            except:
                raise BackendError("erreur lors de la modification du libellé")
            try:
                self.content = self.formulaire(groupe,serveurs,modules,selection)
            except Exception as e:
                raise FrontendError(str(e))

            self.content += """<p><a href="/serveur/del_groupe?id=%s">Suppression globale de ce groupe</a><br>""" % id_groupe
            self.content += """<a href="/serveur/user_groupe">retour à la liste des groupes</a></p>"""

        except Exception as e:
            self.content = e

        return self.content

class DelGroupe(Design):
    """suppression d'un groupe de serveurs
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"del_groupe" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Suppression du groupe"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                # on récupère le groupe passé en parametre
                id_groupe = escape(request.args[b'id'][0])
            except Exception:
                raise FrontendError("id du groupe")
            try:
                # on supprime le groupe de la base de données
                result=backend(proxy(request).serveurs.del_group(int(id_groupe)))
                self.content = """<p><span id="message">Groupe supprimé</span></p>"""
            except Exception as e:
                raise BackendError("erreur de suppression du groupe")

            self.content += """<p><a href="/serveur/user_groupe">Retour à la liste des groupes enregistrés</a></p>"""

        except Exception as e:
            self.content = e

        return self.content


class AfficheAlertes(Design):
    """affiche une liste des serveurs en erreur
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"aff_alertes" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Serveurs en alerte"

    def wmfactory_menu(self, request):
        return Navigation(aide="howto/page7.html").menu()

    def wmfactory_content(self, request):
        return self.content

    def sort_function(self,a,b):
        """comparaison de 2 serveurs
        Le tri se fait sur le n° rne et le module
        """
        etats = [2,3,4,0]
        if a[5]==b[5]:
            if a[0] > b[0]:
                return 1
            elif a[0] < b[0]:
                return -1
            else:
                return 0
        elif etats.index(int(a[5])) > etats.index(int(b[5])):
            return 1
        else:
            return -1

    def formulaire(self, serveurs):
        """affiche les serveurs en erreur (accessibles par l'utilisateur en cours)
        """
        l = ["""<SCRIPT LANGUAGE="JavaScript">
        <!--
        function clear() {
                location.reload(false)
                }
        setTimeout ("clear()", 60000);
        // -->
        </SCRIPT>"""]
        l.append("""<FORM METHOD='POST' ACTION='/serveur/aff_alertes'><center><p><h1>Serveurs en alerte</h1>
        <input type="hidden" name="etat" value="all"/><input type="submit" value="Sélectionner tous les serveurs comme groupe"/></FORM></p></center>
        <FORM METHOD='POST' ACTION='/serveur/aff_alertes'><input type='submit' value='Rafraichir'/></form>""")
        l.append("""<table cellpadding='15' cellspacing='0' align='center' valign='middle' width='100%'><!--contenu2--><tr><td align=center>""")
        l.append("""<table id="tab_alert">""")
        # l.append("""<table color="#9aacd5" border="1"><tr><td align="center">etablissement</td><td align="center">serveur</td><td align="center">erreur</td><td align="center">module</td>""")
        if serveurs == []:
            l.append("""<tr><td><span id="message">Pas de serveur en alerte</span>""")
        else:
            type_err = ""
            for serveur in serveurs:
                # test du type d'erreur : - 2 : timeout
                #                         - 0 : erreur des agents
                #                         - 3 : commandes bloquées
                #                         - 4 : commandes bloquées + erreur des agents
                # serveur : (rne, etablissement, libelle, module, id, status)
                if serveur[5] != type_err:
                    type_err = serveur[5]
                    # affichage du type d'erreur
                    if serveur[5] == 2:
                        etat = "Serveur(s) n'ayant pas contacté le serveur Zéphir dernièrement"
                    elif serveur[5] == 0:
                        etat = """Serveur(s) ayant remonté des alertes (cf page d'état du serveur)"""
                    elif serveur[5] == 3:
                        etat = "Serveur(s) n'ayant pas exécuté les commandes en attente"
                    else:
                        etat = "Serveur(s) ayant des commandes bloquées et des alertes dans les statistiques"
                    l.append("""<tr><td><font size=+1 color="red">%s</font></td>""" % etat)
                    l.append("""<td align="right"><FORM METHOD='GET' ACTION='/serveur/aff_alertes'>
                    <input type="hidden" name="etat" value="%s"/>
                    <input type="submit" value="Sélectionner comme groupe"/></FORM></td></tr>""" % serveur[5])
                string = """<tr><td>%s - <font size=-1>%s</font></td><td>%s (<a href="/serveur/etat?id=%s">%s - %s</a>)</td></tr>""" % (serveur[0],serveur[1],serveur[2],serveur[4],serveur[4],serveur[3])
                l.append(string)

        l.append("""</td></tr></table>""")
        l.append("""</table><!--fin:contenu2-->""")
        l.append("""<p><a href="/serveur">Retour à la gestion des serveurs</a></p>""")

        return "\n".join(l)

    def renderView(self, request):
        # session dans self.prefs
        self.prefs=request.getSession()
        # on récupère l'utilisateur courant
        user = get_user(request)
        # on regarde si il faut stocker un groupe de serveurs en alerte
        try:
            groupe_type = escape(request.args[b'etat'][0])
        except:
            groupe_type = None
        # récupération de la liste des serveurs ayant des problèmes
        try:
            serveurs = backend(proxy(request).serveurs.get_alertes())
            serveurs.sort(key=cmp_to_key(self.sort_function))
            # liste des serveurs a mettre dans un groupe
            if groupe_type != None:
                gr_ids = []
                for serveur in serveurs:
                    if groupe_type == str(serveur[5]) or groupe_type == "all":
                        gr_ids.append(serveur[4])
                if groupe_type == 'all':
                    self.prefs.groupe_alerte = True
                else:
                    # prise en compte du groupe de serveurs
                    groupe = backend(proxy(request).serveurs.groupe_reload(gr_ids))
                    # stockage dans la session
                    self.prefs.groupe_serveur = groupe
                    if hasattr(self.prefs, 'groupe_alerte'):
                        delattr(self.prefs, 'groupe_alerte')
            try:
                self.content = self.formulaire(serveurs)
            except xmlrpclib.ProtocolError:
                self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
            except Exception as e:
                self.content = "erreur frontend : %s" % str(e)
        except Exception as e:
            self.content = e
        return self.content

class AfficheMigration(Design):
    """affiche une liste des serveurs en cours de migration/non migrés
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"aff_migration" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "État de la migration des serveurs"

    def wmfactory_menu(self, request):
        return Navigation(aide="howto/page9.html").menu()

    def wmfactory_content(self, request):
        return self.content

    def sort_function(self,a,b):
        """comparaison de 2 serveurs
        Le tri se fait sur : l'état de migration, la version du module, le libellé du module et le n° rne
        """
        etats = [0,1]
        if a[5] == b[5]:
            # état identique : tri sur le module
            try:
                libelle_a, version_a = a[3].split('-')
                version_a = float(version_a)
                libelle_b, version_b = b[3].split('-')
                version_b = float(version_b)
            except:
                libelle_a = a[3]
                libelle_b = b[3]
                version_a = version_b = 0
            # version de module EOLE (1.5, 2.1, 2.2, ...)
            if version_a > version_b:
                return -1
            elif version_a < version_b:
                return 1
            else:
                # version identique : libellé du module
                if libelle_a > libelle_b:
                    return 1
                elif libelle_a < libelle_b:
                    return -1
                else:
                    # si module identique : tri sur rne
                    if a[0] > b[0]:
                        return 1
                    elif a[0] < b[0]:
                        return -1
                    else:
                        return 0
        elif etats.index(int(a[5])) > etats.index(int(b[5])):
            return 1
        else:
            return -1

    def formulaire(self, serveurs_migration):
        """affiche les serveurs en attente de migration (accessibles par l'utilisateur en cours)
        """
        l = ["""<SCRIPT LANGUAGE="JavaScript">
        <!--
        function clear() {
                location.reload(false)
                }
        setTimeout ("clear()", 60000);
        // -->
        </SCRIPT>"""]
        l.append("""<h1>Serveurs en attente de migration</h1>""")
        l.append("""<br><form method='POST' action='/serveur/aff_migration'><input type='submit' value='Recalculer'/></form>""")
        l.append("""<table cellpadding='15' cellspacing='0' align='center' valign='middle' width='100%'><!--contenu2--><tr><td align=center>""")
        l.append("""<table id="tab_migration">""")
        for mod_vers, serveurs in list(serveurs_migration.items()):
            distrib = config.DISTRIBS[int(mod_vers)][1]
            if len(serveurs[0]) + len(serveurs[1]) == 0:
                l.append("""<tr><td>Pas de serveur EOLE %s à migrer.""" % distrib)
            else:
                l.append("""<tr><td class="td_titre" colspan=2 align='center'>Migration des serveurs EOLE %s""" % distrib)
                for type_serv in [0,1]:
                    if len(serveurs[type_serv]) > 0:
                        if type_serv == 0:
                            etat = """%s serveurs EOLE %s préparés pour la migration""" % (len(serveurs[type_serv]), distrib)
                        else:
                            etat = """%s serveurs EOLE %s à migrer sans donnée spécifique à la migration""" % (len(serveurs[type_serv]), distrib)
                        l.append("""<tr><td><font size=+1 color="red">%s</font></td>""" % etat)
                        l.append("""<td align="right"><FORM METHOD='GET' ACTION='/serveur/aff_migration'>
                        <input type="hidden" name="etat" value="%s"/>
                        <input type="hidden" name="mod_version" value="%s"/>
                        <input type="submit" value="sélectionner comme groupe"/></FORM></td></tr>""" % (type_serv, mod_vers))
                        for serveur in serveurs[type_serv]:
                            # affichage de l'état de migration
                            string = """<tr><td>%s - <font size=-1>%s</font></td><td>%s (<a href="/serveur/etat?id=%s">%s - %s</a>)</td></tr>""" % (serveur[0],serveur[1],serveur[2],serveur[4],serveur[4],serveur[3])
                            l.append(string)
        l.append("""</td></tr></table>""")
        l.append("""</table><!--fin:contenu2-->""")
        return "\n".join(l)

    def renderView(self, request):
        # session dans self.prefs
        self.prefs=request.getSession()
        # on récupère l'utilisateur courant
        user = get_user(request)
        # on regarde si il faut stocker un groupe de serveurs en alerte
        try:
            groupe_type = int(escape(request.args[b'etat'][0]))
            mod_version = int(escape(request.args[b'mod_version'][0]))
        except:
            groupe_type = None
            mod_version = None
        # récupération de la liste des serveurs ayant des problèmes
        try:
            serveurs_migration = backend(proxy(request).serveurs.get_migration_status())
            for mod_vers, serveurs in list(serveurs_migration.items()):
                serveurs[0].sort(key=cmp_to_key(self.sort_function))
                serveurs[1].sort(key=cmp_to_key(self.sort_function))
                # liste des serveurs a mettre dans un groupe
                if groupe_type != None and str(mod_version) == str(mod_vers):
                    gr_ids = [serv[4] for serv in serveurs[groupe_type]]
                    # prise en compte du groupe de serveurs
                    groupe = backend(proxy(request).serveurs.groupe_reload(gr_ids))
                    # stockage dans la session
                    self.prefs.groupe_serveur = groupe
                    if hasattr(self.prefs, 'groupe_alerte'):
                        delattr(self.prefs, 'groupe_alerte')
            try:
                self.content = self.formulaire(serveurs_migration)
            except xmlrpclib.ProtocolError:
                self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
            except Exception as e:
                self.content = "erreur frontend : %s" % str(e)
        except Exception as e:
            traceback.print_exc()
            self.content = e
        return self.content

class RevertServ(Design):
    """restauration d'un serveur suite à une migration Eole1 -> NG
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"revert_serv" :
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Annulation de migration"

    def wmfactory_content(self, request):
        return self.content

    def renderView(self, request):
        try:
            try:
                # on récupère le groupe passé en parametre
                id_serveur = escape(request.args[b'id'][0])
            except Exception:
                raise FrontendError("id du serveur")
            confirm = False
            if b"confirm_revert" in request.args:
                if escape(request.args[b'confirm_revert'][0]) == "1":
                    confirm = True
            if confirm == True:
                try:
                    # on demande un retour au module d'origine
                    result=backend(proxy(request).serveurs.revert_migration(int(id_serveur)))
                    self.content = """<p>Données et clé de connexion restaurées</p>"""
                except Exception as e:
                    raise BackendError("Erreur de restauration des données")
            else:
                self.content = """<p>L'annulation de la migration remettra en place les données et les informations
                de connexion sauvegardées au moment du basculement vers le nouveau module
                <br><font color="red">!! Cette procédure n'est pas réversible, les données du nouveau module seront perdues !!</font></br></p>
                <p><center><FORM METHOD="POST" ACTION="revert_serv">
                <INPUT type="hidden" name="id" value="%s"/>
                <INPUT type="hidden" name="confirm_revert" value="1"/>
                <INPUT type="SUBMIT" value="Confirmer"></FORM>
                </center></p>""" % id_serveur

            self.content += """<p><a href="/serveur/aff?id=%s">Retour à la description du serveur</a></p>""" % id_serveur

        except Exception as e:
            self.content = e

        return self.content

class InfoMaj(Design):
    """Affichage de la liste des paquets installés
    """
    isLeaf = True

    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"info_maj":
            return self
        return Serveur()

    def wmfactory_title(self,request):
        return "Détail des paquets installés"

    def wmfactory_menu(self, request):
        return Navigation().menu()

    def wmfactory_content(self, request):
        return self.content

    def sort_function(self,a,b):
        """comparaison de 2 serveurs
        Le tri se fait sur le n° rne et le module
        """
        if a[0] > b[0]:
            return 1
        elif a[0] < b[0]:
            return -1
        else:
            return 0

    def formulaire(self, paquets):
        """affiche les serveurs eole 1 (accessibles par l'utilisateur en cours)
        """
        old_pkg = []
        new_pkg = []
        ok_pkg = []
        for paq in paquets:
            paq_state = apt_pkg.version_compare(paq[1],paq[2])
            if paq_state < 0:
                old_pkg.append(paq)
            elif paq_state > 0:
                new_pkg.append(paq)
            else:
                ok_pkg.append(paq)
        if len(paquets) == 0:
            l = ["""<p><span id="message">Pas d'information sur les paquets installés.</span></p>"""]
        else:
            l=["""<table cellpadding='15' cellspacing='0' align='center' valign='middle' width='100%'><!--contenu2--><tr><td align=center>"""]
            l.append("""<p><a href="javascript:history.back()">Retour à la page précédente</a></p>""")
            l.append("""<br><table class="tab_paquets">""")
            if old_pkg != []:
                l.append("""<tr><td colspan="3" align="center"><font size=+1 color="red">paquets nécessitant une mise à jour</font></td></tr>""")
                l.append("""<tr><td align="center">Nom du paquet</td><td align="center">Version installée</td><td>Version du serveur de mise à jour</td></tr>""")
                for paq in old_pkg:
                    l.append("""<tr><td>%s</td><td>%s</td><td>%s</td></tr>""" % (paq[0], paq[1], paq[2]))
            if new_pkg != []:
                l.append("""<tr><td colspan="3" align="center"><font size=+1 color="purple">paquets plus récents que la version stable</font></td></tr>""")
                l.append("""<tr><td align="center">Nom du paquet</td><td align="center">Version installée</td><td>Version du serveur de mise à jour</td></tr>""")
                for paq in new_pkg:
                    l.append("""<tr><td>%s</td><td>%s</td><td>%s</td></tr>""" % (paq[0], paq[1], paq[2]))
            if ok_pkg != []:
                # section des paquets à jour
                l.append("""<tr><td colspan="3" align="center"><font size=+1 color="green">paquets à jour</font></td><tr>""")
                l.append("""<tr><td align="center">Nom du paquet</td><td align="center" colspan="2">Version installée</td></tr>""")
                for paq in ok_pkg:
                    l.append("""<tr><td>%s</td><td colspan="2">%s</td></tr>""" % (paq[0], paq[1]))
            l.append("""</table>""")
            l.append("""</td></tr></table><!--fin:contenu2-->""")
        l.append("""<p><a href="javascript:history.back()">Retour à la page précédente</a></p>""")
        return "\n".join(l)

    def renderView(self, request):
        # session dans self.prefs
        self.prefs=request.getSession()
        # on récupère l'utilisateur courant
        user = get_user(request)
        # récupération de la liste des paquets installés
        apt_pkg.init_system()
        try:
            # on récupère le groupe passé en parametre
            id_serveur = escape(request.args[b'id'][0])
        except Exception:
            raise FrontendError("id du serveur")
        try:
            paquets = backend(proxy(request).serveurs.get_maj_infos(int(id_serveur),True))
            paquets.sort(key=cmp_to_key(self.sort_function))
            self.content = self.formulaire(paquets)
        except xmlrpclib.ProtocolError:
            self.content = """<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>"""
        except Exception as e:
            self.content = "erreur frontend : %s" % str(e)
        except Exception as e:
            traceback.print_exc()
            self.content = e
        return self.content

