forked from ScoDoc/ScoDoc
debut form editition utilisateur
This commit is contained in:
parent
d40a2b43cd
commit
46cef02b39
@ -20,6 +20,8 @@ from app import db, login
|
||||
|
||||
from app.scodoc.sco_permissions import Permission
|
||||
from app.scodoc.sco_roles_default import SCO_ROLES_DEFAULTS
|
||||
import app.scodoc.sco_utils as scu
|
||||
from app.scodoc import sco_etud # a deplacer dans scu
|
||||
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
@ -207,6 +209,21 @@ class User(UserMixin, db.Model):
|
||||
def is_administrator(self):
|
||||
return self.active and self.has_permission(Permission.ScoSuperAdmin, None)
|
||||
|
||||
# Some useful strings:
|
||||
def get_nomplogin(self):
|
||||
"""nomplogin est le nom en majuscules suivi du prénom et du login
|
||||
e.g. Dupont Pierre (dupont)
|
||||
"""
|
||||
if self.nom:
|
||||
n = sco_etud.format_nom(self.nom)
|
||||
else:
|
||||
n = scu.strupper(self.user_name)
|
||||
return "%s %s (%s)" % (
|
||||
n,
|
||||
sco_etud.format_prenom(self.prenom),
|
||||
self.user_name,
|
||||
)
|
||||
|
||||
|
||||
class AnonymousUser(AnonymousUserMixin):
|
||||
def has_permission(self, perm, dept=None):
|
||||
|
@ -18,7 +18,6 @@ from flask_login import login_required
|
||||
from flask import current_app
|
||||
|
||||
import app
|
||||
from app.auth.models import Permission
|
||||
|
||||
|
||||
class ZUser(object):
|
||||
@ -111,6 +110,8 @@ def permission_required(permission):
|
||||
|
||||
|
||||
def admin_required(f):
|
||||
from app.auth.models import Permission
|
||||
|
||||
return permission_required(Permission.ScoSuperAdmin)(f)
|
||||
|
||||
|
||||
|
@ -46,7 +46,7 @@ def sidebar_common(context, REQUEST=None):
|
||||
"authuser": str(authuser),
|
||||
}
|
||||
H = [
|
||||
'<a class="scodoc_title" href="about">ScoDoc</a>',
|
||||
'<a class="scodoc_title" href="about">ScoDoc 8</a>',
|
||||
'<div id="authuser"><a id="authuserlink" href="%(ScoURL)s/Users/user_info_page">%(authuser)s</a><br/><a id="deconnectlink" href="%(ScoURL)s/acl_users/logout">déconnexion</a></div>'
|
||||
% params,
|
||||
sidebar_dept(context, REQUEST),
|
||||
|
@ -140,10 +140,10 @@ def do_formsemestre_createwithmodules(context, REQUEST=None, edit=False):
|
||||
)
|
||||
|
||||
# Liste des enseignants avec forme pour affichage / saisie avec suggestion
|
||||
userlist = [sco_users.user_info(u) for u in sco_users.get_user_list()]
|
||||
userlist = sco_users.get_user_list()
|
||||
login2display = {} # user_name : forme pour affichage = "NOM Prenom (login)"
|
||||
for u in userlist:
|
||||
login2display[u["user_name"]] = u["nomplogin"]
|
||||
login2display[u.user_name] = u.get_nomplogin()
|
||||
allowed_user_names = login2display.values() + [""]
|
||||
#
|
||||
formation_id = REQUEST.form["formation_id"]
|
||||
|
@ -261,7 +261,7 @@ def _user_list(user_name):
|
||||
|
||||
|
||||
def user_info(user_name=None, user=None):
|
||||
"""Donne infos sur l'utilisateur (qui peut ne pas etre dans notre base).
|
||||
"""Dict avec infos sur l'utilisateur (qui peut ne pas etre dans notre base).
|
||||
Si user_name est specifie, interroge la BD. Sinon, user doit etre un dict.
|
||||
"""
|
||||
if user_name:
|
||||
@ -294,10 +294,7 @@ def user_info(user_name=None, user=None):
|
||||
if "password_hash" in info:
|
||||
del info["password_hash"]
|
||||
#
|
||||
if info["prenom"]:
|
||||
p = format_prenom(info["prenom"])
|
||||
else:
|
||||
p = ""
|
||||
if info["nom"]:
|
||||
n = format_nom(
|
||||
info["nom"], uppercase=False
|
||||
|
@ -663,6 +663,7 @@ sco_publish(
|
||||
"/formsemestre_edit_options",
|
||||
sco_formsemestre_edit.formsemestre_edit_options,
|
||||
Permission.ScoView,
|
||||
methods=["GET", "POST"],
|
||||
)
|
||||
sco_publish(
|
||||
"/formsemestre_change_lock",
|
||||
|
@ -34,8 +34,11 @@ Vues s'appuyant sur auth et sco_users
|
||||
Emmanuel Viennet, 2021
|
||||
"""
|
||||
|
||||
import jaxml
|
||||
|
||||
from flask import g
|
||||
from flask import current_app, request
|
||||
from flask_login import current_user
|
||||
|
||||
from app.auth.models import Permission
|
||||
from app.auth.models import User
|
||||
@ -48,8 +51,10 @@ from app.decorators import (
|
||||
ZRequest,
|
||||
)
|
||||
|
||||
from app.scodoc import html_sco_header
|
||||
from app.scodoc import sco_users
|
||||
from app.scodoc import sco_utils as scu
|
||||
from app.scodoc.notes_log import log
|
||||
|
||||
from app.views import users_bp as bp
|
||||
|
||||
@ -79,9 +84,320 @@ def user_info(user_name, format="json", REQUEST=None):
|
||||
return scu.sendResult(REQUEST, info, name="user", format=format)
|
||||
|
||||
|
||||
@bp.route("/create_user_form")
|
||||
def create_user_form():
|
||||
raise NotImplementedError()
|
||||
@bp.route("/create_user_form", methods=["GET", "POST"])
|
||||
@permission_required(Permission.ScoUsersAdmin)
|
||||
@scodoc7func(context)
|
||||
def create_user_form(context, REQUEST, user_name=None, edit=0):
|
||||
"form. creation ou edit utilisateur"
|
||||
auth_dept = current_user.dept
|
||||
initvalues = {}
|
||||
edit = int(edit)
|
||||
H = [html_sco_header.sco_header(context, REQUEST, bodyOnLoad="init_tf_form('')")]
|
||||
F = html_sco_header.sco_footer(context, REQUEST)
|
||||
if edit:
|
||||
if not user_name:
|
||||
raise ValueError("missing argument: user_name")
|
||||
initvalues = sco_users._user_list(user_name)
|
||||
H.append("<h2>Modification de l'utilisateur %s</h2>" % user_name)
|
||||
else:
|
||||
H.append("<h2>Création d'un utilisateur</h2>")
|
||||
|
||||
is_super_admin = False
|
||||
if current_user.has_permission(Permission.ScoSuperAdmin, g.scodoc_dept):
|
||||
H.append("""<p class="warning">Vous êtes super administrateur !</p>""")
|
||||
is_super_admin = True
|
||||
|
||||
# Noms de roles pouvant etre attribues aux utilisateurs via ce dialogue
|
||||
# si pas SuperAdmin, restreint aux rôles EnsX, SecrX, AdminX
|
||||
#
|
||||
editable_roles = Role.query.all()
|
||||
if is_super_admin:
|
||||
log("create_user_form called by %s (super admin)" % (current_user.user_name,))
|
||||
else:
|
||||
editable_roles = [
|
||||
r for r in editable_roles if r.name in {u"Ens", u"Secr", u"Admin"}
|
||||
]
|
||||
#
|
||||
if not edit:
|
||||
submitlabel = "Créer utilisateur"
|
||||
orig_roles = set()
|
||||
else:
|
||||
submitlabel = "Modifier utilisateur"
|
||||
initvalues["roles"] = initvalues["roles"].split(",") or []
|
||||
orig_roles = set(initvalues["roles"])
|
||||
if initvalues["status"] == "old":
|
||||
editable_roles = set() # can't change roles of a disabled user
|
||||
# add existing user roles
|
||||
displayed_roles = list(editable_roles.union(orig_roles))
|
||||
displayed_roles.sort()
|
||||
disabled_roles = {} # pour desactiver les roles que l'on ne peut pas editer
|
||||
for i in range(len(displayed_roles)):
|
||||
if displayed_roles[i] not in editable_roles:
|
||||
disabled_roles[i] = True
|
||||
|
||||
# log('create_user_form: displayed_roles=%s' % displayed_roles)
|
||||
|
||||
descr = [
|
||||
("edit", {"input_type": "hidden", "default": edit}),
|
||||
("nom", {"title": "Nom", "size": 20, "allow_null": False}),
|
||||
("prenom", {"title": "Prénom", "size": 20, "allow_null": False}),
|
||||
]
|
||||
if auth_name != user_name: # no one can't change its own status
|
||||
descr.append(
|
||||
(
|
||||
"status",
|
||||
{
|
||||
"title": "Statut",
|
||||
"input_type": "radio",
|
||||
"labels": ("actif", "ancien"),
|
||||
"allowed_values": ("", "old"),
|
||||
},
|
||||
)
|
||||
)
|
||||
if not edit:
|
||||
descr += [
|
||||
(
|
||||
"user_name",
|
||||
{
|
||||
"title": "Pseudo (login)",
|
||||
"size": 20,
|
||||
"allow_null": False,
|
||||
"explanation": "nom utilisé pour la connexion. Doit être unique parmi tous les utilisateurs.",
|
||||
},
|
||||
),
|
||||
(
|
||||
"passwd",
|
||||
{
|
||||
"title": "Mot de passe",
|
||||
"input_type": "password",
|
||||
"size": 14,
|
||||
"allow_null": False,
|
||||
},
|
||||
),
|
||||
(
|
||||
"passwd2",
|
||||
{
|
||||
"title": "Confirmer mot de passe",
|
||||
"input_type": "password",
|
||||
"size": 14,
|
||||
"allow_null": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
else:
|
||||
descr += [
|
||||
(
|
||||
"user_name",
|
||||
{"input_type": "hidden", "default": initvalues["user_name"]},
|
||||
),
|
||||
("user_id", {"input_type": "hidden", "default": initvalues["user_id"]}),
|
||||
]
|
||||
descr += [
|
||||
(
|
||||
"email",
|
||||
{
|
||||
"title": "e-mail",
|
||||
"input_type": "text",
|
||||
"explanation": "vivement recommandé: utilisé pour contacter l'utilisateur",
|
||||
"size": 20,
|
||||
"allow_null": True,
|
||||
},
|
||||
)
|
||||
]
|
||||
|
||||
if not auth_dept:
|
||||
# si auth n'a pas de departement (admin global)
|
||||
# propose de choisir le dept du nouvel utilisateur
|
||||
# sinon, il sera créé dans le même département que auth
|
||||
descr.append(
|
||||
(
|
||||
"dept",
|
||||
{
|
||||
"title": "Département",
|
||||
"input_type": "text",
|
||||
"size": 12,
|
||||
"allow_null": True,
|
||||
"explanation": """département d\'appartenance de l\'utilisateur (s'il s'agit d'un administrateur, laisser vide si vous voulez qu'il puisse créer des utilisateurs dans d'autres départements)""",
|
||||
},
|
||||
)
|
||||
)
|
||||
can_choose_dept = True
|
||||
else:
|
||||
can_choose_dept = False
|
||||
if edit:
|
||||
descr.append(
|
||||
(
|
||||
"d",
|
||||
{
|
||||
"input_type": "separator",
|
||||
"title": "L'utilisateur appartient au département %s"
|
||||
% auth_dept,
|
||||
},
|
||||
)
|
||||
)
|
||||
else:
|
||||
descr.append(
|
||||
(
|
||||
"d",
|
||||
{
|
||||
"input_type": "separator",
|
||||
"title": "L'utilisateur sera crée dans le département %s"
|
||||
% auth_dept,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
descr += [
|
||||
(
|
||||
"date_expiration",
|
||||
{
|
||||
"title": "Date d'expiration", # j/m/a
|
||||
"input_type": "date",
|
||||
"explanation": "j/m/a, laisser vide si pas de limite",
|
||||
"size": 9,
|
||||
"allow_null": True,
|
||||
},
|
||||
),
|
||||
(
|
||||
"roles",
|
||||
{
|
||||
"title": "Rôles",
|
||||
"input_type": "checkbox",
|
||||
"vertical": True,
|
||||
"allowed_values": displayed_roles,
|
||||
"disabled_items": disabled_roles,
|
||||
},
|
||||
),
|
||||
(
|
||||
"force",
|
||||
{
|
||||
"title": "Ignorer les avertissements",
|
||||
"input_type": "checkbox",
|
||||
"explanation": "passer outre les avertissements (homonymes, etc)",
|
||||
"labels": ("",),
|
||||
"allowed_values": ("1",),
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
if "tf-submitted" in REQUEST.form and not "roles" in REQUEST.form:
|
||||
REQUEST.form["roles"] = []
|
||||
if "tf-submitted" in REQUEST.form:
|
||||
# Ajoute roles existants mais non modifiables (disabled dans le form)
|
||||
# orig_roles - editable_roles
|
||||
REQUEST.form["roles"] = list(
|
||||
set(REQUEST.form["roles"]).union(orig_roles - editable_roles)
|
||||
)
|
||||
|
||||
tf = TrivialFormulator(
|
||||
REQUEST.URL0,
|
||||
REQUEST.form,
|
||||
descr,
|
||||
initvalues=initvalues,
|
||||
submitlabel=submitlabel,
|
||||
cancelbutton="Annuler",
|
||||
)
|
||||
if tf[0] == 0:
|
||||
return "\n".join(H) + "\n" + tf[1] + F
|
||||
elif tf[0] == -1:
|
||||
return REQUEST.RESPONSE.redirect(context.UsersURL())
|
||||
else:
|
||||
vals = tf[2]
|
||||
roles = set(vals["roles"]).intersection(editable_roles)
|
||||
if REQUEST.form.has_key("edit"):
|
||||
edit = int(REQUEST.form["edit"])
|
||||
else:
|
||||
edit = 0
|
||||
try:
|
||||
force = int(vals["force"][0])
|
||||
except:
|
||||
force = 0
|
||||
|
||||
if edit:
|
||||
user_name = initvalues["user_name"]
|
||||
else:
|
||||
user_name = vals["user_name"]
|
||||
# ce login existe ?
|
||||
err = None
|
||||
users = _user_list(user_name)
|
||||
if edit and not users: # safety net, le user_name ne devrait pas changer
|
||||
err = "identifiant %s inexistant" % user_name
|
||||
if not edit and users:
|
||||
err = "identifiant %s déjà utilisé" % user_name
|
||||
if err:
|
||||
H.append(tf_error_message("""Erreur: %s""" % err))
|
||||
return "\n".join(H) + "\n" + tf[1] + F
|
||||
|
||||
if not force:
|
||||
ok, msg = context._check_modif_user(
|
||||
edit,
|
||||
user_name=user_name,
|
||||
nom=vals["nom"],
|
||||
prenom=vals["prenom"],
|
||||
email=vals["email"],
|
||||
roles=vals["roles"],
|
||||
)
|
||||
if not ok:
|
||||
H.append(
|
||||
tf_error_message(
|
||||
"""Attention: %s (vous pouvez forcer l'opération en cochant "<em>Ignorer les avertissements</em>" en bas de page)"""
|
||||
% msg
|
||||
)
|
||||
)
|
||||
|
||||
return "\n".join(H) + "\n" + tf[1] + F
|
||||
|
||||
if edit: # modif utilisateur (mais pas passwd)
|
||||
if (not can_choose_dept) and vals.has_key("dept"):
|
||||
del vals["dept"]
|
||||
if vals.has_key("passwd"):
|
||||
del vals["passwd"]
|
||||
if vals.has_key("date_modif_passwd"):
|
||||
del vals["date_modif_passwd"]
|
||||
if vals.has_key("user_name"):
|
||||
del vals["user_name"]
|
||||
if (auth_name == user_name) and vals.has_key("status"):
|
||||
del vals["status"] # no one can't change its own status
|
||||
|
||||
# traitement des roles: ne doit pas affecter les roles
|
||||
# que l'on en controle pas:
|
||||
for role in orig_roles:
|
||||
if role and not role in editable_roles:
|
||||
roles.add(role)
|
||||
|
||||
vals["roles"] = ",".join(roles)
|
||||
|
||||
# ok, edit
|
||||
log("sco_users: editing %s by %s" % (user_name, auth_name))
|
||||
# log('sco_users: previous_values=%s' % initvalues)
|
||||
# log('sco_users: new_values=%s' % vals)
|
||||
context._user_edit(user_name, vals)
|
||||
return REQUEST.RESPONSE.redirect(
|
||||
"userinfo?user_name=%s&head_message=Utilisateur %s modifié"
|
||||
% (user_name, user_name)
|
||||
)
|
||||
else: # creation utilisateur
|
||||
vals["roles"] = ",".join(vals["roles"])
|
||||
# check identifiant
|
||||
if not re.match(r"^[a-zA-Z0-9@\\\-_\\\.]+$", vals["user_name"]):
|
||||
msg = tf_error_message(
|
||||
"identifiant invalide (pas d'accents ni de caractères spéciaux)"
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
# check passwords
|
||||
if vals["passwd"] != vals["passwd2"]:
|
||||
msg = tf_error_message(
|
||||
"""Les deux mots de passes ne correspondent pas !"""
|
||||
)
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if not sco_users.is_valid_password(vals["passwd"]):
|
||||
msg = tf_error_message("""Mot de passe trop simple, recommencez !""")
|
||||
return "\n".join(H) + msg + "\n" + tf[1] + F
|
||||
if not can_choose_dept:
|
||||
vals["dept"] = auth_dept
|
||||
# ok, go
|
||||
log("sco_users: new_user %s by %s" % (vals["user_name"], auth_name))
|
||||
context.create_user(vals, REQUEST=REQUEST)
|
||||
|
||||
|
||||
@bp.route("/import_users_form")
|
||||
@ -90,6 +406,34 @@ def import_users_form():
|
||||
|
||||
|
||||
@bp.route("/user_info_page")
|
||||
@permission_required(Permission.ScoUsersView)
|
||||
@scodoc7func(context)
|
||||
def user_info_page(user_name, REQUEST=None):
|
||||
return sco_users.user_info_page(context, user_name=user_name, REQUEST=REQUEST)
|
||||
|
||||
|
||||
@bp.route("/get_user_list_xml")
|
||||
@permission_required(Permission.ScoView)
|
||||
@scodoc7func(context)
|
||||
def get_user_list_xml(context, dept=None, start="", limit=25, REQUEST=None):
|
||||
"""Returns XML list of users with name (nomplogin) starting with start.
|
||||
Used for forms auto-completion."""
|
||||
userlist = sco_users.get_user_list(dept=dept)
|
||||
start = scu.suppression_diacritics(unicode(start, "utf-8"))
|
||||
start = scu.strlower(str(start))
|
||||
# TODO : à refaire avec une requete SQL #py3
|
||||
# (et en json)
|
||||
userlist = [
|
||||
user
|
||||
for user in userlist
|
||||
if scu.suppress_accents(scu.strlower(user.nom)).startswith(start)
|
||||
]
|
||||
if REQUEST:
|
||||
REQUEST.RESPONSE.setHeader("content-type", scu.XML_MIMETYPE)
|
||||
doc = jaxml.XML_document(encoding=scu.SCO_ENCODING)
|
||||
doc.results()
|
||||
for user in userlist[:limit]:
|
||||
doc._push()
|
||||
doc.rs(user["nomplogin"], id=user["user_id"], info="")
|
||||
doc._pop()
|
||||
return repr(doc)
|
||||
|
Loading…
Reference in New Issue
Block a user