# -*- 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
#
# modules.py
#
# page web de gestion des modules Eole
#
###########################################################################
from twisted.web import static
from twisted.internet import defer
from zephir.web.template.design import Design, proxy, get_user
from zephir.web.config import Navigation, DISTRIBS, file_icons, \
     CREOLE_VERSIONS, PATH_DEF_MODULES, allowed_migrations
from zephir.web.html.erreur import *
from zephir.web.html.dicos import gen_liste_dicos, gen_available_dicos
from zephir.rapports import rapport
import os, base64, traceback
from hashlib import md5
from zephir.eolerpclib import xmlrpclib

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


class AddModule(Design):
    """Ajout d'un module **page 1**
    """
    isLeaf = True
    def getChild(self, name, request):
        if static.isDangerous(name):
            return static.dangerousPathError
        if name == b"add" :
            return self
        return Module()

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

    def wmfactory_content(self, request):
        # définition des différentes versions de modules disponibles
        versions = list(DISTRIBS.keys())
        versions.reverse()
        dist_options = ['<option value="%s" selected="selected">%s (%s)</option>' % (versions[0],DISTRIBS[versions[0]][1],DISTRIBS[versions[0]][0])]
        # on ne permet plus la création des modules 1.X
        for dist in versions[1:-1]:
            dist_options.append('<option value="%s">%s (%s)</option>' % (dist,DISTRIBS[dist][1],DISTRIBS[dist][0]))
        return """
<h1><center>Entrez le nom du nouveau module</h1><br>
<script LANGUAGE="JavaScript"><!--
function validfields(form)
{
    //getting the values:
    if (form.module.value.length > 36)
    {
        alert("libelle : 36 caractères maximum !");
        form.module.focus();
        return false;
    }
    else
    {
        return true;
    }
}
//--></script>
<FORM METHOD=POST ACTION="add2" onSubmit="return validfields(this)">
<table cellpadding="15" cellspacing="0" align="center" valign="middle" >
<tr><td colspan="2">
<input type=text size=20 name="module" value="">-<select name="version">
%s
</select></td></tr>
<tr><td><input name="submit" type="submit" value="Ok"></td>
<td><input name="initialiser" value="Initialiser" type="reset" />
</td></tr>
<tr><td colspan="2"><a href="/module">Retour à la page de gestion des modules</a></td></tr>
</table>
</form>
""" % '\n'.join(dist_options)

class AddModule2(Design):
    """Ajout d'un module **page 2**
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Nouveau Module - Insertion" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                module = escape(request.args[b'module'][0])
                version = int(escape(request.args[b'version'][0]))
                assert module
                assert version
                module = "%s-%s" % (module, DISTRIBS[version][1])
            except:
                raise FrontendError
            # insertion du module dans la base
            try:
                id = backend(proxy(request).modules.add_module(u(module), version))
                self.content = """<p>Le module <b>%s</b> a bien été ajouté<br>
                avec la clef : <b>%s</b></p>
                <p><a href="addvar?id=%s">Ajouter une variante</a><br/>
                <a href="/module">Retour à la page des gestion des modules</a>
                </p>""" % (module, id, id)
            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 Module(Design):
    """Page d'acceuil du conteneur module,
    et Récupération de la liste des modules
    """
    def __init__(self, *args, **kwargs):
        self.entites = {
             'add':AddModule
            ,'add2':AddModule2
            # ,'get':GetModule()
            ,'edit':EditModuleDesc
            ,'edit2':EditModuleIns
            ,'edit_fichier':EditFichier
            ,'save_file':SaveFile
            ,'save_perms':SavePerms
            ,'del_perms':DeletePerms
            ,'del':DelModule
            ,'deleted':DeletedModule
            ,'addvar':AddVarianteDesc
            ,'addvar2':AddVarianteIns
            ,'getvar':GetVariante
            ,'delvar':DelVariante
            ,'deletedvar':DeletedVariante
            ,'editvar':EditVarianteDesc
            ,'editvar2':EditVarianteIns
            ,'importvar':ImportVarianteDesc
            ,'importvar2':ImportVarianteIns
            ,'rapport':RapportModule
            ,'rapport_var':RapportVariante
            ,'var_upgrade':VariantesUpgrade
            ,'migration_modules':MigrationModules
            }
        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 "Liste des modules"

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

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

    def _dump_html(self, liste, stats, dictpool_modules):
        """
        Affichage de la liste des modules
        """
        s = """<table id="tab_liste_module" cellpadding="1" cellspacing="2">
        <tr><td class="col" align="center">Libellé</td>
        <td class="col" align="center">Identifiant</td>
        <td class="col" colspan=3></td>
        <td class="col" align="center">Nb de serveurs</td></tr>"""
        # calcul du nombre de serveurs pour chaque distribution
        serv_distrib = {}
        for d in liste:
            d['nb'] = stats.get(str(d['id']), 0)
            serv_distrib[d['version']] = serv_distrib.get(d['version'], 0) + d['nb']
        # parcours des modules
        current = ''
        for i,d in enumerate(liste):
            try:
                if d['nb'] > 0:
                    # ne pas proposer la suppression si des serveurs existent
                    d['del_style'] = 'class="not-active"'
                else:
                    d['del_style'] = ''
                if d['version'] != current:

                        # affichage des informations de distribution
                        libelle_distrib = "EOLE-%s (Ubuntu %s)" % (DISTRIBS[d['version']][1], DISTRIBS[d['version']][0])
                        current = d['version']
                        maintained = DISTRIBS[d['version']][2]
                        # si plus de serveurs dans cette version, on propose une suppression globale
                        if serv_distrib[current] == 0:
                            del_dist = """<tr><td colspan="2" align="center"><form action="/module/del" method="POST">
                            <input type="hidden" name="version" value="%s"/>
                            <input type="submit" value="Supprimer tous les modules"/></form></td></tr>""" % (str(current),)
                        else:
                            del_dist = ""
                        # bouton d'accès à la bibliothèque de dictionnaires
                        if str(current) in dictpool_modules:
                            link_dicos = """<form action=/dicos method="POST">
                            <input type="hidden" name="module_version" value="%s"/>
                            <input type="submit" value="Dictionnaires personnalisés"/></form>""" % (str(current),)
                        else:
                            link_dicos = ""
                        prev_version = d['version'] - 1
                        if prev_version in DISTRIBS and current >= 5 and \
                        not current in allowed_migrations.get(prev_version, []):
                            # bouton d'accès à la page d'import des personnalisations (entre 2 releases successives,
                            # sauf si l'adaptation de configuration est nécessaire )
                            link_upgrade = """<form action=/module/migration_modules method="POST">
                            <input type="hidden" name="module_version" value="%s"/>
                            <input type="submit" value="Import des données %s"/></form>""" % (str(d['version']), DISTRIBS[prev_version][1])
                        else:
                            link_upgrade = ""
                        if link_dicos or link_upgrade:
                            dist_actions = """<table><tr><td>%s</td><td>%s</td></tr>%s</table>""" % (link_dicos, link_upgrade, del_dist)
                        elif del_dist:
                            dist_actions = """<table>%s</table>""" % (del_dist,)
                        else:
                            dist_actions = ""
                        if maintained == False:
                            s+="""<tr><td class="version_modules_non_maintenu" colspan='6' align='center'>%s - non maintenu%s</td></tr>""" % (libelle_distrib, dist_actions)
                        else:
                            s+="""<tr><td class="version_modules_maintenu" colspan='6' align='center'>%s%s</td></tr>""" % (libelle_distrib, dist_actions)

                s += """
                <tr>
                <td>%(libelle)s</td>
                <!-- <td>%(version)s</td> -->
                <td>%(id)s</td>
                <td><a href="/module/edit?id=%(id)s">modifier</a></td>
                <td><a href="/module/del?id=%(id)s" %(del_style)s>supprimer</a></td>
                <td><a href="/module/getvar?id=%(id)s">variantes</a></td>
                <td align='center'>%(nb)d</td>
                <!-- <td><a href="/services/get?id=%(id)s">services</a></td> -->
                </tr>
                """ % d
            except KeyError:
                liste.pop(i)
                log.msg("Le module "+ d['libelle'] + " (version : " + str(d['version']) + ") est une version obsolète")

        s += """</table>
        <p><FORM METHOD="POST" ACTION="/module/add">
        <input type="submit" value="Ajouter un module"></form><br/><br/>
        <a href="/module/rapport">Générer un rapport <img src="/images/pdf.png"></a><br/>
        </p>
        """
        # <p><a href="/module/add">Ajouter un module</a><br/><br/>
        return s

    def renderView(self, request):
        try:
            try:
                liste = backend(proxy(request).modules.get_module())
                stats = backend(proxy(request).get_stats())
                dictpool_modules = backend(proxy(request).dicos.managed_modules())
                self.content = """<h1>Liste des modules</h1>%s""" % self._dump_html(liste, stats['serv_modules'], dictpool_modules)
            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 RapportModule(Design):
    """Liste des modules en pdf
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Rapport Module" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                nom_pdf = rapport.Rapport(proxy(request)).pdf("modules")
                self.content = """
                <p>
                <a href="/tmp/%s">Télécharger le rapport <img 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 EditModuleDesc(Design):
    """Edition (modification) d'un module : formulaire de saisie
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Module - Edition" % get_user(request)

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

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

    def _dump_html(self, id_mod, libelle, version, dicos, defaults, available):
        """Formulaire d'edition du module
        """
        get_dictname = 'false'
        # formulaire de suppression des dictionnaires
        if defaults is None:
            # gestion à l'ancienne : ajout d'un fichier dictionnaire dans le répertoire du module
            add_dic_form = """<tr><td>Dictionnaire</td>
<td colspan="2" align="center"><INPUT NAME="dictionnaire" TYPE="file"/>
<input type="hidden" name="dico_name" value=""></td></tr>
            """
            list_avail = ""
            get_dictname = 'true'
        else:
            add_dic_form = ""
            list_avail = ""
# XXX Evolution possible : réactiver et éditer le fichier de default_modules ?
#                list_avail = """<tr><td colspan="3" align="center">
#<FORM METHOD=POST ACTION="/module/edit">
#<INPUT type="hidden" name="id" value="%s">
#<INPUT type="SUBMIT" value="Ajouter un dictionnaire/paquet"/>
#<select name="add_dict">%s</select>
#</FORM></td></tr>""" % (id_mod, gen_available_dicos(dicos, available))
        if dicos == []:
            dic_form = ""
        else:
            dic_form = """<tr><td colspan="3" align="center"></td></tr>
%s<tr><td colspan="3" align="left">
<FORM METHOD=POST ACTION="/module/edit">
<INPUT type="hidden" name="id" value="%s">""" % (list_avail, id_mod)
            if defaults is not None:
                dic_mod, dic_orig = gen_liste_dicos(dicos, defaults)
                if dic_mod:
                    dic_form += "<hr><ul><li>%s</li>" % "</li><li>".join(dic_mod)
                    dic_form += """</ul><INPUT type="SUBMIT" value="Supprimer la sélection"><hr>"""
                dic_form += """<hr><div align="center">Dictionnaires du module
<p>Correspond aux dictionnaires présents à l'installation du module.<br>
Pour installer des dictionnaires supplémentaires sur les serveurs enregistrés, utiliser une variante.</p>
<ul><li>%s</li></ul>""" % "</li><li>".join(dic_orig)
                dic_form += """Pour modifier cette liste, éditer %s<br>
et relancer le service zephir (ne pas modifier les modules livrés par défaut)""" % os.path.join(PATH_DEF_MODULES, str(version), libelle)
            else:
                dic_form += """<hr><div align="center">Dictionnaires du module</div><br>"""
                for dico in dicos:
                    dic_form += """%s<INPUT TYPE="checkbox" NAME="del_%s"><br>""" % (dico, os.path.splitext(dico)[0])
                dic_form += """<br><INPUT type="SUBMIT" value="Supprimer la sélection">"""
            dic_form += """</td></tr></FORM>"""

        creole_version = CREOLE_VERSIONS[version]
        if creole_version == "creole3":
            conf_window = "javascript:void(window.open('/genconfig1/module/%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_mod
        else:
            conf_window = "javascript:void(window.open('/genconfig/module/%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_mod
        varconf_form = """<button onClick="%s">Changer les valeurs par défaut avec l'interface de configuration</button>""" % conf_window

        return """
<h1>Modification des données du module</h1>
<table cellpadding="6" cellspacing="0" align="center" valign="middle" >
<tr><td colspan="2" align="center">%s</td></tr>
%s
<script LANGUAGE="JavaScript"><!--
function validfields(form, get_dictname)
{
    //getting dictionnary file name
    if (get_dictname) {
        var val_dico = document.file_form.dictionnaire.value;
        document.file_form.dico_name.value = val_dico;
    }
    //getting the values:
    if (form.libelle.value.length > 40)
    {
        alert("libelle : 40 caractères maximum !");
        form.libelle.focus();
        return false;
    }
    else
    {
        return true;
    }
}
//--></script>
<tr><td colspan="2" align="center"><hr>
<FORM METHOD=POST ACTION="edit2" name="file_form" enctype="multipart/form-data" onSubmit="return validfields(this, %s)"></td></tr>
<tr><td>Libellé</td><td>
<input type=text size=20 name="libelle" value="%s">
<input type="hidden" name="id" value="%s"></td></tr>
%s
<tr><td><input type="submit" value="Mettre à jour"></td>
<td><input name="initialiser" value="Initialiser" type="reset" /></td></tr>
<tr><td colspan="2" align="center"><a href="/module/get">Retour à la liste des modules</a></td></tr>
</form>
</table>
<br />
""" % (varconf_form, dic_form, get_dictname, libelle, id_mod, add_dic_form)


    def renderView(self, request):
        try:
            try:
                id_mod = escape(request.args[b'id'][0])
                assert id_mod
            except:
                raise FrontendError("identifiant")
            try:
                # supression de dictionnaires si demandé
                for param in request.args:
                    param = param.decode()
                    if param == 'add_dict' and request.args.get(b'add_dict',[b''])[0].decode() != '':
                        try:
                            dict_type, dict_name = escape(request.args[b'add_dict'][0]).split('_', 1)
                        except:
                            log.msg('paramètre invalide (add_dict) : %s' % str(request.args[b'add_dict'].decode()))
                        else:
                            backend(proxy(request).dicos.add_module(int(id_mod), dict_type, dict_name))
                    if param.startswith('removedict_'):
                        prefix, dict_type, dict_name = param.split('_', 2)
                        backend(proxy(request).dicos.del_module(int(id_mod), dict_type, dict_name))
                    # ancienne méthode de suppression
                    if param.startswith('del_'):
                        dict_name = param[param.index('_')+1:]+'.xml'
                        backend(proxy(request).modules.del_mod_dict(int(id_mod),dict_name))
                resultat = backend(proxy(request).modules.get_module(int(id_mod)))
                module = resultat[0]
                # récupération des dictionnaires depuis dictpool si le module l'utilise
                dicos = backend(proxy(request).dicos.list_module(int(id_mod)))
                defaults = backend(proxy(request).dicos.get_module_defaults(int(id_mod)))
                available = backend(proxy(request).dicos.list_available(module['version']))
                self.content = self._dump_html(module['id'],module['libelle'], module['version'], dicos, defaults, available)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError(str(e))

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

        return self.content



class EditModuleIns(Design):
    """Edition d'un module - message au backend
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Édition d'un module"

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


    def renderView(self, request):
        try:
            try:
                id_mod = escape(request.args[b'id'][0])
                libelle = escape(request.args[b'libelle'][0])
                assert id_mod and libelle
            except:
                raise FrontendError("identifiant ou libellé")

            try:
                mod = backend(proxy(request).modules.get_module(id_mod))[0]
                if libelle != mod['libelle']:
                    resultat = backend(proxy(request).modules.edit_module(id_mod, u({'libelle':libelle})))
                self.content = """
                <p><span id="message">Le module a bien été modifié</span></p>
                <p>
                <a href="/module/edit?id=%s">Retour à l'édition</a><br/>
                <a href="/module">Retour à la page de gestion des modules</a>
                </p>
                """ % id_mod

            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 DelModule(Design):
    """Confirmation de suppression d'un module
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Module (Suppression)" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                id_module = escape(request.args.get(b'id', [""])[0])
                version = escape(request.args.get(b'version', [""])[0])
                assert id_module or version
            except:
                raise FrontendError("identifiant du module à supprimer")

            if id_module:
                self.content = """
                <p><span id="message">Voulez-vous vraiment supprimer le module %s ?</span></p>
                <p><a href="deleted?id=%s">Supprimer</a> / <a href="/module">Annuler</a></p>
                """ % (id_module, id_module)
            else:
                self.content = """
                <p><span id="message">Voulez-vous vraiment supprimer tous les modules de la version %s ?</span></p>
                <p><a href="deleted?version=%s">Supprimer</a> / <a href="/module">Annuler</a></p>
                """ % (DISTRIBS[int(version)][1], version)

        except Exception as e:
            self.content = e

        return self.content


class DeletedModule(Design):
    """Suppression effective d'un module
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Module - Suppression" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                id_module = escape(request.args.get(b'id', [""])[0])
                version = escape(request.args.get(b'version', [""])[0])
                assert id_module or version
            except:
                raise FrontendError("identifiant")
            if version:
                # récupération de la liste des modules concernés
                dist_label = DISTRIBS[int(version)][1]
                try:
                    dist_modules = backend(proxy(request).modules.modules_distrib(dist_label))
                    modules = [mod['id'] for mod in dist_modules]
                except xmlrpclib.ProtocolError:
                    self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
                except Exception as e:
                    raise BackendError("Erreur lors de la recherche des modules de cette version : %s" % str(e))
            else:
                modules = [id_module]
            # suppression du(des) module(s) de la base
            self.content = ""
            erreurs = []
            for module in modules:
                try:
                    backend(proxy(request).modules.del_module(int(module)))
                    self.content += """Le module %s a bien été supprimé<br/>""" % module
                except Exception as e:
                    erreurs.append(str(module))
            if erreurs:
                if len(erreurs) == 1:
                    self.content += """<br/><font color="red">Erreur lors de la suppression du module %s </font><br/>""" % erreurs[0]
                else:
                    self.content += """<br/><font color="red">Erreur lors de la suppression des modules suivants : </font> %s<br/>""" % ", ".join(erreurs)
                self.content += """<p>Vérifiez que vous avez les permissions nécessaires et qu'aucun serveur de ce type n'existe</p>"""
            self.content += """<p><a href="/module/get">Retour à la liste des modules</a></p>"""
        except Exception as e:
            self.content = e

        return self.content


class AddVarianteDesc(Design):
    """Formulaire de renseignements sur la variante
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Nouvelle variante"

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

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

    def _dump_html(self, id) :
        return """
<h1>Ajout d'une variante</h1>
<script LANGUAGE="JavaScript"><!--
function validfields(form)
{
    //getting the values:
    if (form.libelle.value.length > 40)
    {
        alert("libelle : 40 caractères maximum !");
        form.libelle.focus();
        return false;
    }
    else
    {
        return true;
    }
}
//--></script>
<FORM METHOD=POST ACTION="addvar2" onSubmit="return validfields(this)">
<table cellpadding="15" cellspacing="0" align="center" valign="middle" >
<tr><td>Libellé</td><td>
<input type="text" size="20" name="libelle" value=""></td></tr>
<tr><td>Mot de passe</td>
<td><input type="password" size="20" name="pass_var" value=""></td></tr>
<tr><td>Identifiant de la variante source (copie)</td>
<td><input type="text" size="4" name="var_src" value=""></td></tr>
<tr><td>
<input type="hidden" name="module" value="%s">
<INPUT type=SUBMIT value="Ok">
</td>
<td><input name="initialiser" value="Initialiser" type="reset" /></td>
</tr>
</table>
</form>
<p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>

""" % (id, id)

    def renderView(self, request):
        try:
            self.id = escape(request.args[b'id'][0])
            if self.id :
                self.content = self._dump_html(self.id)
            else:
                 self.content = """
            <p>vous n'avez pas renseigné l'identifiant du module<br/>
            <a href="javascript:history.back()">Retour</a></p>
            """
        except Exception as e:
            self.content = e

        return self.content


class AddVarianteIns(Design):
    """Ajout de la variante
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Nouvelle variante"

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


    def renderView(self, request):
        try:
            try:
                var_src = ""
                self.libelle = escape(request.args[b'libelle'][0])
                self.pass_var = md5(escape(request.args[b'pass_var'][0]).encode()).hexdigest()
                self.module = escape(request.args[b'module'][0])
                assert self.libelle and self.module
            except:
                raise FrontendError("libellé ou mot de passe")
            else:
                # on regarde si il faut faire une copie de variante
                try:
                    var_src = escape(request.args[b'var_src'][0])
                    assert var_src
                except:
                    pass
                else:
                    try:
                        var_src = int(var_src)
                    except:
                        raise TypeError("l'identifiant de variante source doit être un entier")
            try:
                if var_src != "":
                    id_var = backend(proxy(request).modules.copy_variante(int(self.module),var_src,u(self.libelle),u(self.pass_var)))
                    self.content = """
                    <p><span id="message">La variante %s a bien été ajoutée avec la clé %s</span></p>
                    <p>Le contenu de la variante %s a été importé avec succès</p>
                    <p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>
                    """ % (self.libelle,id_var,var_src,self.module)
                else:
                    id_var = backend(proxy(request).modules.add_variante(int(self.module), u(self.libelle),u(self.pass_var)))
                    self.content = """
                    <p><span id="message">La variante %s a bien été ajoutée avec la clé %s</span></p>
                    <p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>
                    """ % (self.libelle,id_var,self.module)

            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except BackendError as err:
                raise err
            except:
                raise BackendError()

        except Exception as e:
            self.content = e
        return self.content

class GetVariante(Design):
    """Récupération de la liste des variantes pour un module
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Liste des variantes" % get_user(request)

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

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

    def _dump_html(self,id_mod,liste,variantes_migration,stats):
        """Affichage de la liste des variantes
        """
        head_mods = ""
        if variantes_migration != {}:
            # headers des modules migrables
            mods_dst = list(variantes_migration.keys())
            mods_dst.sort()
            for mod_dst in mods_dst:
                libel_mod, available, selected = variantes_migration[mod_dst]
                head_mods += "<td>équivalence %s</td>" % libel_mod
            head_mods += "<td></td>"
        s = """<table cellpadding="1" cellspacing="2"><tr><td class="col" align="center">Libellé</td><td class="col" align="center">Clé</td><td class="col">Serveurs</td><td class="col" colspan="3" align="center">Édition</td>%s</tr>""" % head_mods
        for d in liste:
            # choix des variantes de migration
            if variantes_migration != {}:
                mods_dst = list(variantes_migration.keys())
                mods_dst.sort()
                if d['libelle'] == 'standard':
                    d['std_colspan'] = str(4 + len(variantes_migration))
                var_mig = ""
                for mod_dst in mods_dst:
                    libel_mod, available, selected = variantes_migration[mod_dst]
                    var_mig += """<form action="/module/var_upgrade" method="POST"><td><select name="upgrade_%s_%s">""" % (d['id'], mod_dst)
                    if d['id'] in list(selected.keys()):
                        id_sel = selected[d['id']]
                        var_mig += """<option value="">- non défini -</option>"""
                    else:
                        id_sel = None
                        var_mig += """<option selected="selected" value="">- non défini -</option>"""
                    for var_dst, lib_dst in available:
                        if id_sel and var_dst == id_sel:
                            var_mig += """<option selected="selected" value="%s">%s (%s)</option>""" % (var_dst, lib_dst, var_dst)
                        else:
                            var_mig += """<option value="%s">%s (%s)</option>""" % (var_dst, lib_dst, var_dst)
                    var_mig += """</select></td>"""
                var_mig += """<td><input type="hidden" name="id_mod" value="%s"/><input type="hidden" name="id_src" value="%s"/><input type="submit" value="sauver ces correspondances"/></td></form>""" % (id_mod, d['id'])
                d['migration'] = var_mig
            else:
                d['std_colspan'] = "3"
                d['migration'] = ""
            d['stats'] = stats[str(d['id'])]
            # on interdit la modification de la variante standard
            if d['libelle'] == 'standard':
                modele ="""<tr><td>%(libelle)s</td><td>%(id)s</td><td align="center">%(stats)s</td>
                <td colspan="%(std_colspan)s" align="center"><i>non modifiable</i></td></tr>""" % d
            else:
                modele = """
                <tr><td>%(libelle)s</td><td>%(id)s</td><td align="center">%(stats)s</td>
                <td><a href="editvar?id=%(id)s&module=%(module)s">modifier</a></td>
                <td><a href="delvar?id=%(id)s&module=%(module)s">supprimer</a></td>
                <td><a href="importvar?id=%(id)s&module=%(module)s">importer/copier</a></td>
                %(migration)s</tr>
                """ % d
            s += modele

        s += """</table>
        """ % d
        return s

    def renderView(self, request):
        try:
            try:
                # identifiant du module
                id_mod = escape(request.args[b'id'][0])
                assert id_mod
                id_mod = int(id_mod)
            except Exception:
                raise FrontendError("identifiant")
            try:
                liste_backend = backend(proxy(request).modules.get_variante())
                stats = backend(proxy(request).get_stats())['serv_variantes']
                variantes_migration = {}
                try:
                    # variantes de migration
                    infos_migration = backend(proxy(request).modules.get_migration_infos(id_mod))
                    # mise en forme
                    if infos_migration != []:
                        libel_vars = {}
                        for variante in liste_backend:
                            libel_vars[variante['id']] = variante['libelle']
                        variantes_migration = {}
                        for mod_version, available, selected in infos_migration:
                            if DISTRIBS[mod_version][2] == True:
                                # on en propose pas la migration vers une distribution non maintenue
                                var_selected = {}
                                libel_version = DISTRIBS[mod_version][1]
                                vars_available = []
                                for var_src, var_dst in selected:
                                    var_selected[var_src] = (var_dst)
                                for var_avail in available:
                                    vars_available.append((var_avail, libel_vars[var_avail]))
                                variantes_migration[mod_version] = (libel_version, vars_available, var_selected)
                except:
                    traceback.print_exc()
                # filtrage : les variantes du module
                liste = []
                for d in liste_backend:
                    if d['module'] == id_mod:
                        liste.append(d)
                try:
                    # la liste est vide
                    # normalement, ça n'arrive JAMAIS
                    self.content = """
                    <p>Ce module ne contient pas encore de variante.</p>>
                    <p><a href="addvar?id=%s">Ajouter une variante</a></p>
                    <p><a href="/module">Retour au menu des modules</a></p>
                    """ % id_mod
                    assert not liste
                except:
                    # récupération du libelle du module
                    mod = backend(proxy(request).modules.get_module(id_mod))
                    libelle_module = mod[0]['libelle']
                    self.content = """<h1>Liste des variantes relatives au module %s</h1>%s
                    <p><a href="addvar?id=%s">Ajouter une variante</a></p>
                    <p><a href="rapport_var?module=%s">Générer un rapport <img src="/images/pdf.png"></a></p>
                    <p><a href="/module">Retour &agrave la liste des modules</a></p>
                    """ % (libelle_module,self._dump_html(id_mod,liste,variantes_migration,stats),id_mod,id_mod)

            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except Exception as err:
                traceback.print_exc()
                raise BackendError(err)

        except Exception as e:
            self.content = e

        return self.content

class RapportVariante(Design):
    """Liste des variantes d'un module en pdf
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Rapport variantes"

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

    def renderView(self, request):
        try:
            try:
                self.module = escape(request.args[b'module'][0])
                assert not self.module == ""
            except Exception:
                raise FrontendError("identifiant du module")
            try:
                nom_pdf = rapport.Rapport(proxy(request)).pdf('variante_module',self.module)
                self.content = """<p><span id="message">Le rapport a été généré</span></p>
                <p><a href="/tmp/%s">Télécharger le rapport <img src="/images/pdf.png"></a><br/></p>
		<p><a href="javascript:history.back()">Retour à la liste des variantes</a><br/></p>
                """ % os.path.basename(nom_pdf)
            except xmlrpclib.ProtocolError:
                raise BackendError("""<p><span id="alerte">Vous n'êtes pas autorisé à effectuer cette action</span></p>""")
            except:
                raise BackendError
        except (FrontendError, BackendError) as e:
            self.content = e
        return self.content

class DelVariante(Design):
    """Confirme la suppression d'une variante
    """
    isLeaf = True

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

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

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

    def renderView(self, request):
        try:
            try:
                id = escape(request.args[b'id'][0])
                id_module = escape(request.args[b'module'][0])
            except:
                raise FrontendError("identifiant")

            self.content = """
            <p><span id="message">Voulez-vous vraiment supprimer la variante %s ?</span></p>
            <p><a href="deletedvar?id=%s&module=%s">Supprimer</a> / <a href="/module/getvar?id=%s">Annuler</a></p>
            """ % (id,id,id_module,id_module)

        except Exception as e:
            self.content = e

        return self.content


class DeletedVariante(Design):
    """Suppression effective d'une variante
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Suppressien de la variante"

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

    def renderView(self, request):
        try:
            try:
                id = escape(request.args[b'id'][0])
                id_module = escape(request.args[b'module'][0])
            except:
                raise FrontendError("identifiant")

            try:
                backend(proxy(request).modules.del_variante(int(id)))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except DatabaseError as e:
                raise BackendError("Veuillez vérifier si la variante à supprimer n'est pas en cours d'utilisation.<BR>")
            except Exception as e:
                raise BackendError

            self.content = """
            <p><span id="message">La variante a bien été supprimée</span></p>
            <p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>
            """ % id_module


        except Exception as e:
            self.content = e

        return self.content



class EditVarianteDesc(Design):
    """Edition d'une variante (formulaire de saisie)
    """
    isLeaf = True

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

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

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

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

    def _dump_html(self, id_var, id_module, libelle, fichiers, owner, mod_version, dicos, dicos_mod, available, user_ok=False, is_admin=False, removed_exists=False):
        """Formulaire d'edition
        """
        # constitution de l'affichage des fichiers (plus lien de suppression)
        if user_ok == False:
            pass_form = """<br/><FORM name="passform" onSubmit="return false;">Mot de passe <input type="password" name="passwd" value=""/>(si variante créée par un autre utilisateur)</FORM><br/>"""
        else:
            pass_form = """<FORM name="passform" onSubmit="return false;"><input type="hidden" name="passwd" value=""/></FORM>"""
        affiche={}
        get_dictname = "false"
        # calcul du formulaire d'ajout des dictionnaires/paquets disponibles
        if dicos is not None:
            add_dict_form = """<select name="add_dict">%s</select>""" % gen_available_dicos(dicos + dicos_mod, available)
        else:
            add_dict_form = """<INPUT NAME="dico" TYPE="file"/>"""
            get_dictname = "true"
        for type_f in ['dicos_var','persos_var','patchs_var','fichiers_var','rpms_var']:
            ask_remove = 'true'
            if mod_version < 5 or type_f not in ('fichiers_var', 'persos_var'):
                ask_remove = 'false'
            l=["""<table width="100%">"""]
            container_files = {'':[]}
            if type_f == 'dicos_var' and dicos is not None:
                # si des dictionnaires sont définis, on affiche la liste
                if dicos:
                    # gestion de l'affichage avec le pool de dictionnaires
                    dic_var = gen_liste_dicos(dicos, [])[0]
                    form_dic_var = """%s<p>(Cocher pour supprimer)</p>""" % "<br/>".join(dic_var)
                    l.append(form_dic_var)
            else:
                liste_fichiers = fichiers[type_f]
                # tri des fichiers
                liste_fichiers.sort(key=lambda x:x[0])
                for f in liste_fichiers:
                    if type_f != 'rpms_var':
                        if type_f == 'fichiers_var':
                            # cas particulier des fichiers persos : icone selon le type et la présence ou non
                            f, f_info = f
                            container = ""
                            # gestion des conteneurs
                            if ('::') in f:
                                if f.split('::')[1] not in ('root', ''):
                                    f_name, container = f.split('::')
                                else:
                                    f_name = f.split('::')[0]
                            else:
                                f_name = f
                            f_img = file_icons.get(f_info,'gnome-mime-text.png')
                            if container not in container_files:
                                container_files[container] = ["""<tr><td colspan="2"><b>conteneur %s</b></td></tr>""" % container]
                            if f_info == 'missing':
                                # fichier non présent sur zephir
                                container_files[container].append("""<tr><td><img 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:
                                container_files[container].append("""<tr><td><img src="/images/%s"><a href="edit_fichier?id=%s&module=%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_var,id_module,f,type_f,f_name,f,type_f,ask_remove))
                        else:
                            l.append("""<tr><td><a href="edit_fichier?id=%s&module=%s&file=%s&type=%s">%s</a></td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (id_var,id_module,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 and f in [dico.rsplit(os.sep, 2)[-2] for dico in dicos]:
                            l.append("""<tr><td colspan="2">"""+f+"</td></tr>")
                        else:
                            l.append("<tr><td>"+f+"""</td><td align="right">(<a href="javascript:del_file('%s','%s',%s)">supprimer</a>)</td></tr>""" % (f,type_f,ask_remove))
                if type_f in ('fichiers_var'):
                    # affichage de la liste des fichiers divers (par conteneur si besoin)
                    liste_cont = list(container_files.keys())
                    liste_cont.sort()
                    for container in liste_cont:
                        l.extend(container_files[container])
            l.append("</table>")
            affiche[type_f]=l
        if user_ok or is_admin:
            form_owner = """<FORM METHOD="POST" NAME="owner_form" ACTION="editvar2" onSubmit="return validfields(this)">
<input type="hidden" name="id" value="%s">
<input type="hidden" name="module" value="%s">
<tr><td colspan="2">Propri&eacute;taire<br/><input type=text size=20 name="owner" value="%s"><input type="hidden" name="pass_var" value=""/><input type="submit" value="Changer le propriétaire"/></td></tr></form>
<FORM METHOD="POST" NAME="password_form" ACTION="editvar2" onSubmit="return validpass(this)">
<input type="hidden" name="id" value="%s">
<input type="hidden" name="module" value="%s">
<tr><td colspan="2">Mot de passe<br/><input type=password size=20 name="passmd5" value=""><input type="hidden" name="pass_var" value=""/><input type="submit" value="Changer le mot de passe"/><br /><br /></td></tr></form>
    """ % (id_var, id_module, owner, id_var, id_module)
        else:
            form_owner = ""
        if removed_exists:
            input_removed = """<tr><td colspan="3" align="center"><a href="edit_fichier\
?id=%s&module=%s&file=removed&type=%s&localpath=%s">Fichiers à supprimer sur les clients\
</a>&nbsp;(fichiers divers et templates)</td></tr>""" % (id_var, id_module, 'fichiers_var', 'fichiers_zephir')
        else:
            input_removed = ""
        if mod_version < 5:
            input_container = ""
        else:
            input_container = """<br><input type="text" name="fichier_container" value="">conteneur (facultatif)"""
            # lien sur "fichiers_zephir/removed si présent"
        creole_version = CREOLE_VERSIONS[mod_version]
        if creole_version == "creole3":
            conf_window = "javascript:void(window.open('/genconfig1/variante/%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_var
        else:
            conf_window = "javascript:void(window.open('/genconfig/variante/%s','site', 'scrollbars=yes, resizable=yes, location=no, directories=no, status=no'))" % id_var
        varconf_form = """<button onClick="%s">Changer les valeurs par défaut</button>""" % conf_window
        js_funcs = """<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;
    // hack : on force le focus pour valider le champ passwd au cas où on serait encore dedans
    document.forms['libel_form'].libelle.focus();
    var pass_var = document.forms['passform'].passwd.value;
    if ((pass_var != "") && (pass_var != null))
    {
        del_form.pass_var.value = pass_var;
    }
    // on demande si le fichier doit être supprimé sur le client
    if ( ask_remove == true ) {
        if (confirm('Supprimer également le fichier sur les machines cibles ?\\n(Annuler le supprimera seulement sur Zéphir)'))
        {
            del_form.remove.value="true";
        }
    }
    del_form.submit();
    return location;
};
function validfields(form)
{
    // fetching password
    form.pass_var.value = document.forms['passform'].passwd.value;
    return true;
};
function validlibelle(form)
{
    //getting the values:
    if (form.libelle.value.length > 40)
    {
        alert("libelle : 40 caractères maximum !");
        form.libelle.focus();
        return false;
    }
    return validfields(form);
};
function validpass(form)
{
    pass_val = form.passmd5.value;
    if (pass_val == "")
    {
        if (confirm("Attention, les autres utilisateurs ne pourront pas\\néditer cette variante si le mot de passe est vide\\n\\ncontinuer ?") == false)
        {
            return false;
        }
    }
    return validfields(form);
};
function get_names(get_dictname)
{
    //getting the values:
    var val_pass = document.forms['passform'].passwd.value;
    var val_template = document.file_form.template.value;
    var val_patch = document.file_form.patch.value;
    //setting the values:
    document.file_form.pass_var.value = val_pass;
    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>
"""

        return """
<h1>Description de la variante %s nommée %s</h1>
<p>
<table><tr><td>%s</td><td>
<FORM METHOD=POST ACTION="/module/del_perms">
<input type="hidden" name="module" value="%s">
<input type="hidden" name="id" value="%s">
<center><INPUT type="SUBMIT" value="Voir les permissions définies"/></center>
</form></td></tr></table>
</p>
%s
<FORM METHOD="POST" NAME="delform" ACTION="/module/editvar">
<INPUT TYPE="hidden" NAME="id" VALUE="%s">
<INPUT TYPE="hidden" NAME="module" VALUE="%s">
<INPUT TYPE="hidden" NAME="fic_sup" VALUE="">
<INPUT TYPE="hidden" NAME="type" VALUE="">
<INPUT TYPE="hidden" NAME="remove" VALUE="false">
<INPUT TYPE="hidden" NAME="pass_var" VALUE="">
</FORM>
%s
<table cellpadding="5" cellspacing="0" align="center" valign="middle" >
<FORM METHOD="POST" NAME="libel_form" ACTION="editvar2" onSubmit="return validlibelle(this)">
<input type="hidden" name="id" value="%s">
<input type="hidden" name="module" value="%s">
<tr><td colspan="2">Libellé<br/>
<input type=text size=20 name="libelle" value="%s">
<input type="hidden" name="pass_var" value=""/>
<input type="submit" value="Changer le libellé"/></td></tr></form>
%s
<FORM name="file_form" METHOD="POST" ACTION="editvar" enctype="multipart/form-data" onSubmit="javascript:get_names(%s)">
<table id="tab_variante" border="1" cellpadding="5" cellspacing="0" align="center" valign="middle" >
<tr><td align="center" colspan="2">Liste des fichiers</td></tr>%s
<tr><td valign="top">Ajouter des dictionnaires<br/>%s</td><td>%s</td>
<tr><td valign="top">Templates additionnels<br/><INPUT NAME="template" TYPE="file"/></td><td>%s</td>
<tr><td valign="top">Patchs<br/><INPUT NAME="patch" TYPE="file"/></td><td>%s</td>
<tr><td valign="top">Fichiers divers<br/><INPUT NAME="fichier" TYPE="file"/><br><input type="text" name="fichier_name" value="">destination
%s</td><td>%s</td></tr>
<tr><td valign="top">Paquets additionnels<br/><INPUT NAME="rpms" TYPE="text"/></td><td>%s</td>
<input type="hidden" name="dico_name" value="">
<input type="hidden" name="template_name" value="">
<input type="hidden" name="patch_name" value="">
<tr><td colspan="2"><input type="hidden" name="id" value="%s">
<input type="hidden" name="module" value="%s">
<input type="hidden" name="fichiers" value="oui">
<input type="hidden" name="pass_var" value=""/>
<center><INPUT type=SUBMIT value="Valider les modifications"><br>
<input name="initialiser" value="Réinitialiser" type="reset"/></center></td></tr>
</form>
</table>
<p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>
<p><a href="/module/get">Retour à la liste des modules</a></p>
""" % (id_var, libelle, varconf_form, id_module,id_var,pass_form,id_var,id_module,js_funcs,id_var,id_module,libelle,form_owner,get_dictname,input_removed,add_dict_form,"".join(affiche['dicos_var']),"".join(affiche['persos_var']),"".join(affiche['patchs_var']),input_container,"".join(affiche['fichiers_var']),"".join(affiche['rpms_var']),id_var,id_module,id_module)

    @defer.inlineCallbacks
    def renderView(self, request):
        try:
            try:
                id_var = escape(request.args[b'id'][0])
                id_module = escape(request.args[b'module'][0])
            except:
                raise FrontendError("identifiant")
            # mot de passe de la variante en paramètre ?
            try:
                pass_var = md5(escape(request.args[b'pass_var'][0]).encode()).hexdigest()
            except:
                pass_var = None
            # on regarde si une demande d'ajout/suppression de dictionnaires existe
            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_variante(int(id_var), dict_type, dict_name, pass_var or ""))
                    except Exception as e:
                        log.msg("Erreur lors de la supression d'un dictionnaire de la variante %s: %s" % (str(id_var), str(e)))
                if param == 'add_dict' and request.args.get(b'add_dict',[b''])[0].decode() != '':
                    # ajout d'un dictionnaire à la variante
                    try:
                        dict_type, dict_name = escape(request.args[b'add_dict'][0]).split('_', 1)
                        backend(proxy(request).dicos.add_variante(int(id_var), dict_type, dict_name, pass_var or ""))
                    except:
                        log.msg('paramètre invalide (add_dict) : %s' % str(request.args[b'add_dict'][0].decode()))
            # 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:
                # on regarde si on doit ajouter des fichiers
                try:
                    request.args[b'fichiers'][0]
                except:
                    # on arrive depuis une autre page
                    pass
                else:
                    # on a demandé un ajout de fichiers
                    dic_files = {'dicos_var':[],'patchs_var':[],'persos_var':[],'fichiers_var':[],'rpms_var':[]}
                    # on récupère les noms et contenus des fichiers
                    try:
                        dic_files['dicos_var'].append([escape(request.args[b'dico_name'][0]),base64.encodebytes(request.args[b'dico'][0])])
                    except:
                        pass
                    try:
                        dic_files['persos_var'].append([escape(request.args[b'template_name'][0]),base64.encodebytes(request.args[b'template'][0])])
                    except:
                        pass
                    try:
                        dic_files['patchs_var'].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_var'].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 Exception("""le chemin du fichier doit être absolu (ex : /tmp/toto)<p><a href="javascript:history.back()">Retour</a></p>""")
                    try:
                        dic_files['rpms_var'].append(escape(request.args[b'rpms'][0]))
                    except:
                        pass
                    # mise en place des fichier
                    if pass_var is not None:
                        backend(proxy(request).modules.add_files(int(id_var),dic_files,u(pass_var)))
                    else:
                        backend(proxy(request).modules.add_files(int(id_var),dic_files))
            else:
                # on demande la suppression du fichier (pas d'exception dans la récupération du formulaire)
                dic_files = {'dicos_var':[],'patchs_var':[],'persos_var':[],'fichiers_var':[],'rpms_var':[]}
                if localpath:
                    fic_sup = [fic_sup, localpath]
                dic_files[type_sup].append(fic_sup)
                # on supprime
                try:
                    if pass_var is not None:
                        del_ok = yield backend(proxy(request).modules.del_files(int(id_var),dic_files,remove_sup,u(pass_var)))
                    else:
                        del_ok = yield backend(proxy(request).modules.del_files(int(id_var),dic_files,remove_sup))
                except xmlrpclib.ProtocolError:
                    raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                except Exception as e:
                    raise BackendError(e)
            zephir_user = get_user(request)
            try:
                dico_fic = backend(proxy(request).modules.fichiers_variante(int(id_var),True))
                resultat = backend(proxy(request).modules.get_variante(int(id_var)))
                d = resultat[0]
                # version du module
                mod_vers = int(backend(proxy(request).modules.get_module(int(d['module'])))[0]['version'])
                # récupération des dictionnaires gérés par DictPool
                dicos = backend(proxy(request).dicos.list_variante(int(id_var)))
                dicos_mod = backend(proxy(request).dicos.list_module(int(d['module'])))
                available = backend(proxy(request).dicos.list_available(mod_vers))
                # vérification des permissions
                is_admin = False
                perms = backend(proxy(request).get_permissions(zephir_user))
                if 4 in perms:
                    # l'utilisateur a les droits de gestion des autorisations
                    is_admin = True
                # vérification de l'existence du fichier fichiers_zephir/removed
                removed_exists = False
                if mod_vers >= 5:
                    try:
                        backend(proxy(request).modules.get_var_file(int(id_var), int(d['module']), 'fichiers_zephir/removed'), False)
                        removed_exists = True
                    except BackendError:
                        pass
            except xmlrpclib.ProtocolError:
                self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
            try:
                if d['owner'] == zephir_user:
                    user_ok = True
                else:
                    user_ok = False
                self.content = self._dump_html(d['id'], id_module, d['libelle'], dico_fic, d['owner'], mod_vers, dicos, dicos_mod, available, user_ok, is_admin, removed_exists)
            except Exception as e:
                traceback.print_exc()
                raise FrontendError(e)
        except Exception as e :
            traceback.print_exc()
            self.content = str(e)

        defer.returnValue(self.content)

class EditVarianteIns(Design):
    """Edition d'une variante (message au backend)
    """
    isLeaf = True

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

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

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


    def renderView(self, request):
        try:
            try:
                id = escape(request.args[b'id'][0])
                id_module = escape(request.args[b'module'][0])
                if b'pass_var' in request.args:
                    pass_var = md5(escape(request.args[b'pass_var'][0]).encode()).hexdigest()
                else:
                    pass_var = None
                id_module = escape(request.args[b'module'][0])
                if b'owner' in request.args:
                    owner = escape(request.args[b'owner'][0])
                    data = {'owner':owner}
                elif b'libelle' in request.args:
                    libelle = escape(request.args[b'libelle'][0])
                    data = {'libelle':libelle}
                else:
                    passmd5 = md5(escape(request.args[b'passmd5'][0]).encode()).hexdigest()
                    data = {'passmd5':passmd5}
            except:
                raise FrontendError("identifiant ou libelle")

            try:
                backend(proxy(request).modules.edit_variante(int(id),u(data),pass_var))
                self.content = """
                <p>La variante a bien été modifiée<br/>
                <a href="/module/getvar?id=%s">Retour à la liste des variantes</a>
                </p>
                """ % (id_module)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            #except Exception, e:
            #    raise BackendError(str(e))

        except Exception as e:
            self.content = e

        return self.content


class ImportVarianteDesc(Design):
    """Import d'une variante distante (formulaire de saisie)
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Import/Copie d'une variante"

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

    def _dump_html(self,id_var,id_module,owner,cred_user):
        """Formulaire d'edition
        """
        l=["""<h1>Paramètres d'import/copie</h1>
<FORM METHOD="POST" ACTION="importvar2">
<input type="hidden" name="module" value="%s">
<input type="hidden" name="id" value="%s">""" %  (id_module,id_var)]

        if owner != cred_user:
            l.append("""mot de passe de la variante à modifier&nbsp;<input type="password" name="passwd" value=""/><br>
(cette variante à été créée par %s)<br/><br/>""" % owner)
        else:
            l.append("""<input type="hidden" name="passwd" value="">""")

        l.append("""<table>
<tr><td><input type="text" name="id_distant" value=""/></td><td>n° de variante à copier (source)</td></tr>
<tr><td colspan=2 align="center"><b>Import depuis un serveur zephir distant</b><br/>(Laisser vide pour une copie locale)</td></tr>
<tr><td><input type="text" name="adr_zephir" value=""/></td><td>adresse zephir distant</td></tr>
<tr><td><input type="text" name="login_distant" value=""/></td><td>utilisateur sur zephir distant</td></tr>
<tr><td><input type="password" name="pwd_distant" value=""/></td><td>(mot de passe correspondant)</td></tr></table>
<p><p><font color="red">Attention, les données de la variante %s seront écrasées</font></p>
<center><INPUT type=SUBMIT value="Importer/Copier">
<input name="initialiser" value="Réinitialiser" type="reset"/></center></p>
</form>
<p><a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>
<p><a href="/module/get">Retour à la liste des modules</a></p>
""" % (id_var,id_module))

        return "".join(l)

    def renderView(self, request):
        try:
            try:
                id_var = escape(request.args[b'id'][0])
                id_module = escape(request.args[b'module'][0])
            except:
                raise FrontendError("identifiant")
            try:
                resultat = backend(proxy(request).modules.get_variante(int(id_var)))
                owner = resultat[0]['owner']
                cred_user = get_user(request)
            except xmlrpclib.ProtocolError:
                self.content = """Vous n'êtes pas autorisé à effectuer cette action"""
            try:
                pass
            except:
                raise FrontendError
            self.content = self._dump_html(id_var,id_module,owner,cred_user)
        except Exception as e :
            self.content = str(e)

        return self.content


class ImportVarianteIns(Design):
    """import réel de la variante
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Confirmation d'import" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                id_var = escape(request.args[b'id'][0])
                module = escape(request.args[b'module'][0])
                pass_var = escape(request.args[b'passwd'][0])
                adr_zephir = escape(request.args[b'adr_zephir'][0])
                login = escape(request.args[b'login_distant'][0])
                pwd = escape(request.args[b'pwd_distant'][0])
                id_src = escape(request.args[b'id_distant'][0])
                # assert id_var and module and adr_zephir and login and pwd and id_src
            except:
                raise FrontendError
            if pass_var != "":
                if not isinstance(pass_var, bytes):
                    pass_var = pass_var.encode()
                pass_var = md5(pass_var).hexdigest()
            if adr_zephir != "":
                # import d'une variante distante
                try:
                    res = backend(proxy(request).modules.import_variante(pass_var,int(id_var),int(id_src),u(adr_zephir),u(login),u(pwd)))
                except:
                    raise BackendError("""Mauvais mot de passe pour la variante locale ou erreur d'accès au serveur distant""")
                self.content = """<p>La variante a bien été importée<br/>"""
            else:
                # copie d'une variante locale
                res = backend(proxy(request).modules.copy_variante(module,id_src,"",pass_var,False,id_var))
                self.content = """<p>La variante a bien été copiée %s -> %s<br/>""" % (str(id_src), str(id_var))
            try:
                self.content += """<a href="/module/getvar?id=%s">Retour à la liste des variantes</a></p>""" % (module)
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError(e)

        except Exception as e:
            self.content = e
        return self.content

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

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

    def wmfactory_title(self,request):
        return "(%s) Edition (fichier de variante)" % get_user(request)

    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_var':'dicos/',
                    'persos_var':'fichiers_perso/',
                    'patchs_var':'patchs/',
                    'fichiers_var':'fichiers_zephir/'
            }
            try:
                filename = escape(request.args[b'file'][0])
                module = escape(request.args[b'module'][0])
                variante = escape(request.args[b'id'][0])
                type_file = escape(request.args[b'type'][0])
                assert variante
                assert module
                assert filename
                assert type_file in list(chemins.keys())
            except Exception as err:
                raise FrontendError(err)
            try:
                localpath=escape(request.args[b'localpath'][0])
            except:
                localpath = ""
            basepath=localpath
            try:
                # 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).modules.get_var_file(variante,module,path,True))
                var_owner = ""
                detail_var = backend(proxy(request).modules.get_variante(int(variante)))
                var_owner = detail_var[0]['owner']
                var_mod = detail_var[0]['module']
                detail_mod = backend(proxy(request).modules.get_module(int(var_mod)))
                mod_version = int(detail_mod[0]['version'])
            except Exception as err:
                raise BackendError(err)

            self.content = """<br/><a href=/module/editvar?id=%s&module=%s>Retour</a>""" % (variante, module)
            # gestion des droits pour les fichiers personnalisés (pas dicos et patchs)
            if (type_file  == 'persos_var') 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_var':
                # récupération des droits actuels du fichier
                file_rights = backend(proxy(request).modules.get_variante_perms(variante, path))
                if file_rights[path] != []:
                    mode, user, group, recursive = file_rights[path]
                if recursive == True:
                    checked = " CHECKED"
                else:
                    checked = ""
                # on affiche le contenu dans une boite de saisie de texte
                self.content += """
                <script LANGUAGE="JavaScript"><!--
                function getpwd(form)
                {
                    //getting passwd from passwd field
                    form.pass_var.value = document.forms['passform'].passwd.value;
                    return true;
                }
                //--></script>"""
                # 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 = ""
                # boite de saisie du mot de passe commune aux deux formulaires
                if ('::') in path:
                    path_info = "%s (conteneur %s)" % (path.split('::')[0], path.split('::')[-1])
                else:
                    path_info = path
                self.content += """<table border="1">
                <FORM METHOD="POST" NAME="delform" ACTION="/module/editvar">
                <INPUT TYPE="hidden" NAME="id" VALUE="%s">
                <INPUT TYPE="hidden" NAME="module" 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">
                <INPUT TYPE="hidden" NAME="pass_var" VALUE="">
                </FORM>
                <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;
                    var pass_var = document.forms['passform'].passwd.value;
                    if ((pass_var != "") && (pass_var != null))
                    {
                        del_form.pass_var.value = pass_var;
                    }
                    // on demande si le fichier doit être supprimé sur le client
                    if ( ask_remove == true ) {
                        if (confirm('Supprimer également le fichier sur les machines cibles ?'))
                        {
                            del_form.remove.value="true";
                        }
                    }
                    del_form.submit();
                    return location;
                };
                </script>
                <FORM METHOD="POST" ACTION="save_perms" onSubmit="return getpwd(this)">
                <INPUT TYPE="hidden" NAME="filename" value="%s">
                <INPUT TYPE="hidden" NAME="id" value="%s">
                <INPUT TYPE="hidden" NAME="module" 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"/><input type="hidden" name="pass_var" value=""></FORM></td></tr></table>""" % (variante, module, localpath, path, variante, module, path_info, mode, recurse_label, user, group, recurse_input)
                if var_owner == get_user(request):
                    self.content += """<FORM name="passform" onSubmit="return false;"><input type="hidden" name="passwd" value=""/></FORM>"""
                else:
                    self.content += """<br><FORM name="passform" onSubmit="return false;">Mot de passe <input type="password" name="passwd" value=""/></FORM>"""
            # 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 = file_icons.get(f_info,'gnome-mime-text.png')
                    if f_info == 'missing':
                        # fichier non présent sur zephir
                        self.content += """<tr><td><img src="/images/%s">%s</td>\n""" % (f_img, fic)
                    else:
                        self.content += """<tr><td><img src="/images/%s"><a href="edit_fichier?id=%s&module=%s&file=%s&type=%s&localpath=%s">%s</a></td>\n""" % (f_img, variante, module, os.path.join(filename,fic),type_file,localpath,fic)
                    # lien de suppression
                    #self.content += """<td align="right">(<a href="editvar?id=%s&module=%s&fic_sup=%s&type=%s">supprimer</a>)</td></tr>\n""" % (variante, module, path+os.sep+fic, type_file)
                    ask_remove = "true"
                    if mod_version < 5 or type_file in ('fichiers_var', 'persos_var'):
                        # 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>"""
            else:
                # formulaire de modification du contenu
                if contenu == "BINARY":
                    self.content += """<p>fichier non éditable (binaire)</p>
                    <a href=/module/editvar?id=%s&module=%s>Retour</a>""" % (variante, module)
                else:
                    self.content += """
                    <FORM METHOD="POST" ACTION="save_file" onSubmit="return getpwd(this)">
                    <INPUT TYPE="hidden" NAME="filename" value="%s">
                    <INPUT TYPE="hidden" NAME="module" value="%s">
                    <INPUT TYPE="hidden" NAME="id" value="%s"><BR/>
                    <INPUT TYPE="hidden" NAME="type" value="%s"><BR/>
                    <INPUT TYPE="hidden" NAME="localpath" value="%s"><BR/>
                    <INPUT TYPE="hidden" NAME="pass_var" value=""/>
                    <textarea wrap="off" NAME="page" COLS="80" ROWS="16" STYLE="width: 90%%; heigth: 80%%">%s</textarea><br>
                    <INPUT TYPE="Submit" value="Sauvegarder"/>
                    </FORM>
                    <br/><a href=/module/editvar?id=%s&module=%s>Retour</a>
                    """ % (filename,module,variante,type_file,basepath,base64.decodebytes(contenu.data).decode(),variante,module)

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

        return self.content

class DeletePerms(Design):
    """Edition du fichier de permissions d'une variante (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 Module()

    def wmfactory_title(self,request):
        return "(%s) Edition des permissions (suppression de permissions)" % get_user(request)

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

    def renderView(self, request):
        try:
            var_owner = ""
            try:
                module = escape(request.args[b'module'][0])
                variante = escape(request.args[b'id'][0])
                module = int(module)
                variante = int(variante)
                detail_var=backend(proxy(request).modules.get_variante(variante))
                var_owner = detail_var[0]['owner']
            except:
                raise FrontendError
            try:
                delpath=escape(request.args[b'del_path'][0])
                pass_var = md5(escape(request.args[b'pass_var'][0]).encode()).hexdigest()
            except:
                delpath=None
            # suppression des fichiers demandés
            if delpath != None:
                if delpath == "droits_zephir":
                    # toutes les permissions
                    backend(proxy(request).modules.del_variante_perms(variante,"",pass_var))
                else:
                    # permissions d'un chemin précis
                    backend(proxy(request).modules.del_variante_perms(variante,delpath,pass_var))
            # récupération du contenu
            permissions=backend(proxy(request).modules.get_variante_perms(variante))
            libel_variantes = self.get_variante(proxy(request))


            # génération du tableau des permissions existantes
            self.content = """<h1>Permissions spécifiques pour la variante %s du module %s</h1>""" % (variante, module)
            #self.content = """<p><a href="/module/editvar?id=%s&module=%s">Retour</a></p>""" % (variante, module)
            self.content += """<script LANGUAGE="JavaScript"><!--
            function getpwd(form)
            {
                //getting passwd from passwd field
                form.pass_var.value = document.forms['passform'].passwd.value;
                return true;
            }
            //--></script>"""
            if len(permissions) > 0:
                self.content += """<table border="1">
                <tr><td colspan="6" align="center"><b>Permissions définies sur la variante %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>""" % (libel_variantes[variante], variante)
                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
                    # formulaire de supression des permissions
                    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>
                    <form method=POST action="/module/del_perms" onSubmit="return getpwd(this)">
                    <input type="hidden" name="id" value="%s">
                    <input type="hidden" name="module" value="%s">
                    <input type="hidden" name="del_path" value="%s">
                    <input type="hidden" name="pass_var" value="">
                    <input type="submit" value="Supprimer"></form>
                    </a></td></tr>""" % (ficperso, perms, user, group, check, variante, module, ficperso)
                if var_owner == get_user(request):
                    self.content += """<form name="passform" onSubmit="return false"><input type="hidden" name="passwd" value=""></form>"""
                else:
                    self.content += """<tr><td colspan="6" align="center"><form name="passform" onSubmit="return false"><input type="password" name="passwd">Mot de passe</form></td></tr>"""
                self.content += """<tr><td colspan="6" align="center">
                <form method=POST action="/module/del_perms" onSubmit="return getpwd(this)">
                <input type="hidden" name="id" value="%s">
                <input type="hidden" name="module" value="%s">
                <input type="hidden" name="del_path" value="droits_zephir">
                <input type="hidden" name="pass_var" value="">
                <input type="submit" value="Tout supprimer"></form>
                </td></tr></table>""" % (variante, module)
                self.content += """<p><a href="/module/editvar?id=%s&module=%s">Retour</a></p>""" % (variante, module)
            else:
                self.content += """<p><span id="message">Pas de permissions spécifiées pour cette variante</span></p>"""
                self.content += """<p><a href="/module/editvar?id=%s&module=%s">Retour</a></p>""" % (variante, module)

        except Exception as e:
            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 Module()

    def wmfactory_title(self,request):
        return "(%s) sauvegarde des permissions" % get_user(request)

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

    def renderView(self, request):
        try:
            try:
                filename = escape(request.args[b'filename'][0])
                variante = escape(request.args[b'id'][0])
                module = escape(request.args[b'module'][0])
                mode = escape(request.args[b'mode'][0])
                user = escape(request.args[b'user'][0])
                group = escape(request.args[b'group'][0])
                pass_var = escape(request.args[b'pass_var'][0])
                if not isinstance(pass_var, bytes):
                    pass_var = pass_var.encode()
                pass_var = md5(pass_var).hexdigest()
                assert variante
                assert module
                assert filename
                if b'recurse' in request.args:
                    recursive = True
                else:
                    recursive = False
            except:
                raise FrontendError

            # sauvegarde des permissions sur zephir
            rights = {filename:[mode, user, group,recursive]}
            backend(proxy(request).modules.set_variante_perms(int(variante),u(rights),u(pass_var)))

            self.content = """Permissions sauvegardées<BR>
            <a href=/module/editvar?id=%s&module=%s>Retour</a>""" % (variante, module)

        except Exception as e:
            self.content = e

        return self.content

class SaveFile(Design):
    """sauvegarde du fichier d'une variante
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) sauvegarde (fichier de variante)" % get_user(request)

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

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

            try:
                pass_var = md5(escape(request.args[b'pass_var'][0]).encode()).hexdigest()
            except:
                pass_var = None

            # sauvegarde du fichier sur zephir
            dic_files = {'dicos_var':[],'patchs_var':[],'persos_var':[],'fichiers_var':[],'rpms_var':[]}
            # 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
            if pass_var is not None:
                backend(proxy(request).modules.add_files(int(variante),dic_files,u(pass_var),True,'save'))
            else:
                backend(proxy(request).modules.add_files(int(variante),dic_files,"",True,'save'))

            self.content = """Fichier sauvegardé<BR>
            <a href=/module/editvar?id=%s&module=%s>Retour</a>""" % (variante,module)

        except Exception as e:
            self.content = e

        return self.content

class VariantesUpgrade(Design):
    """Confirme la correspondance des variantes
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "Variante - Correspondance à l'upgrade"

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

    def renderView(self, request):
        try:
            try:
                id_mod = escape(request.args[b'id_mod'][0])
                var_src = escape(request.args[b'id_src'][0])
                var_migr = []
                for name, val in request.args.items():
                    if name.decode().startswith('upgrade_%s_' % var_src):
                        value = val[0].decode()
                        if value != "":
                            var_migr.append(int(value))
            except:
                traceback.print_exc()
                raise FrontendError("identifiant des variantes")

            print("migration %s -> (%s)" % (var_src, var_migr))
            try:
                backend(proxy(request).modules.variantes_upgrade(var_src, var_migr))
            except xmlrpclib.ProtocolError:
                raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
            except Exception as e:
                raise BackendError(e)
            self.content = """
            <p>Correspondance des variantes sauvegardées</p><br>
            <a href="/module">Retour à la page des modules</a><br><br>
            <a href="/module/getvar?id=%s">Retour à la page des variantes</a><br>""" % id_mod

        except Exception as e:
            self.content = e

        return self.content

class MigrationModules(Design):
    """Gestion de la migration des variantes entre 2 versions de la distribution
    """
    isLeaf = True

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

    def wmfactory_title(self,request):
        return "(%s) Migration des modules" % get_user(request)

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

    def resultat_upgrade(self, res_upgrade, version_src, request):
        """affiche les informations remontées par la fonction upgrade_modules
        """
        content = "<table>"
        dist_src = DISTRIBS[version_src][1]
        no_data = True
        if res_upgrade['dicos']:
            no_data = False
            content += """<tr><td><b>Dictionnaires locaux</b> copiés depuis EOLE %s :</td></tr>
<tr><td align="left"><ul><li>%s</li></ul></td></div>""" % (dist_src, "</li><li>".join(res_upgrade['dicos']))
        if res_upgrade['modules']:
            no_data = False
            content += """<tr><td><b>Valeurs par défaut</b> EOLE %s reprises pour les modules suivants : </td></tr>
<tr><td align="left"><div><ul><li>%s</li></ul></div></td></tr>""" % (dist_src, "</li><li>".join(res_upgrade['modules']))
        if res_upgrade['variantes']:
            no_data = False
            modules = self.get_module(proxy(request))
            all_var = backend(proxy(request).modules.get_variante())
            variantes = {}
            for var in all_var:
                if var['id'] in res_upgrade['variantes']:
                    var_module = variantes.get(var['module'], [])
                    var_module.append('%s (%s)' % (var['libelle'], str(var['id'])))
                    variantes[var['module']] = var_module
            liste_var = []
            for module, mod_vars in list(variantes.items()):
                liste_var.append('%s: %s' % (modules[module][0], ', '.join(mod_vars)))
            content += """<tr><td><b>Nouvelles variantes</b> créées par copie :</td></tr>
<tr><td align="left"><ul><li>%s</li></ul></td></tr>""" % "</li><li>".join(liste_var)
        if res_upgrade['errors']:
            no_data = False
            content += """<tr><td><b>Erreurs rencontrées</b> lors de la copie des variantes :</td></tr>
<tr><td align="left"><ul><li>%s</li></ul></td></tr>""" % "</li><li>".join(res_upgrade['errors'])
        if no_data:
            content += """<tr><td>Aucune donnée à importer</td></tr>"""
        content += "</table>"
        return content

    def form_upgrade(self, version_src, version_dst):
        """Affiche les informations qui seront mises en place
        """
        content = """<p>Cette procédure reporte les personnalisations mises en place sur EOLE %(src)s :<br>
<ul><li>Les dictionnaires locaux de la version %(src)s sont recopiés si ils n'existent pas</li>
<li>Les variante n'existant pas (test sur le libellé) sont copiées et définies comme équivalentes</li>
<li>Les valeurs par défaut des modules %(src)s sont recopiées si rien n'est défini pour EOLE %(dst)s</li></ul></p>""" % \
                  {'src':DISTRIBS[version_src][1], 'dst':DISTRIBS[version_dst][1]}
        content += """<p><form action="/module/migration_modules">
        <input type="hidden" name="module_version" value="%s">
        <input type="hidden" name="do_apply" value="1">
        <input type="submit" value="Importer les données">
        </form></p>""" % version_dst
        # On vérifie que la version n-1 permet une montée en version via Upgrade-Auto
        return content

    def renderView(self, request):
        try:
            try:
                version_dst = int(escape(request.args[b'module_version'][0]))
                assert version_dst in DISTRIBS
            except:
                traceback.print_exc()
                raise FrontendError("distribution de destination")
            try:
                version_src = version_dst - 1
                assert version_src in DISTRIBS
            except:
                traceback.print_exc()
                raise FrontendError("Pas de distribution antérieure")
            do_upgrade = escape(request.args.get(b'do_apply', ['0'])[0]) == '1'

            if version_dst in allowed_migrations.get(version_src, []):
                self.content += """<font color="red">La copie automatique des personalisations n'est pas prise en compte entre EOLE %s et %s</font>""" % \
                (DISTRIBS[version_src][1], DISTRIBS[version_dst][1])
            else:
                self.content = """<p><b>EOLE %s - Import des variantes et valeurs par défaut</b></p>""" % (DISTRIBS[version_dst][1])
                if do_upgrade:
                    # demande validée, lancement de l'upgrade des modules
                    try:
                        res_upgrade = backend(proxy(request).modules.upgrade_modules(version_src, version_dst))
                        self.content += self.resultat_upgrade(res_upgrade, version_src, request)
                    except xmlrpclib.ProtocolError:
                        raise BackendError("""Vous n'êtes pas autorisé à effectuer cette action""")
                    except Exception as e:
                        raise BackendError(e)
                else:
                    self.content += self.form_upgrade(version_src, version_dst)
            self.content += """<p><a href="/module">Retour à la page des modules</a><br><br></p>"""

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

        return self.content


