forked from ScoDoc/ScoDoc
offre expiration automatique, enlever espaces siret, changement formulaires ...
This commit is contained in:
parent
73b46b11dc
commit
77eefbe483
@ -107,9 +107,7 @@ def get_offre_files_and_depts(offre: EntrepriseOffre, depts: list):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def send_email_notifications_entreprise(
|
def send_email_notifications_entreprise(subject, entreprise: Entreprise):
|
||||||
subject, entreprise: Entreprise, correspondant: EntrepriseCorrespondant
|
|
||||||
):
|
|
||||||
txt = [
|
txt = [
|
||||||
"Une entreprise est en attente de validation",
|
"Une entreprise est en attente de validation",
|
||||||
"Entreprise:",
|
"Entreprise:",
|
||||||
@ -119,14 +117,6 @@ def send_email_notifications_entreprise(
|
|||||||
f"\tcode postal: {entreprise.codepostal}",
|
f"\tcode postal: {entreprise.codepostal}",
|
||||||
f"\tville: {entreprise.ville}",
|
f"\tville: {entreprise.ville}",
|
||||||
f"\tpays: {entreprise.pays}",
|
f"\tpays: {entreprise.pays}",
|
||||||
"",
|
|
||||||
"Correspondant:",
|
|
||||||
f"nom: {correspondant.nom}",
|
|
||||||
f"prenom: {correspondant.prenom}",
|
|
||||||
f"telephone: {correspondant.telephone}",
|
|
||||||
f"mail: {correspondant.mail}",
|
|
||||||
f"poste: {correspondant.poste}",
|
|
||||||
f"service: {correspondant.service}",
|
|
||||||
]
|
]
|
||||||
txt = "\n".join(txt)
|
txt = "\n".join(txt)
|
||||||
email.send_email(
|
email.send_email(
|
||||||
@ -192,7 +182,7 @@ def verif_entreprise_data(entreprise_data):
|
|||||||
if data == "":
|
if data == "":
|
||||||
return False
|
return False
|
||||||
if EntreprisePreferences.get_check_siret():
|
if EntreprisePreferences.get_check_siret():
|
||||||
siret = entreprise_data[0].strip() # vérification sur le siret
|
siret = entreprise_data[0].replace(" ", "") # vérification sur le siret
|
||||||
if re.match("^\d{14}$", siret) is None:
|
if re.match("^\d{14}$", siret) is None:
|
||||||
return False
|
return False
|
||||||
try:
|
try:
|
||||||
|
@ -72,7 +72,7 @@ def _build_string_field(label, required=True, render_kw=None):
|
|||||||
class EntrepriseCreationForm(FlaskForm):
|
class EntrepriseCreationForm(FlaskForm):
|
||||||
siret = _build_string_field(
|
siret = _build_string_field(
|
||||||
"SIRET (*)",
|
"SIRET (*)",
|
||||||
render_kw={"placeholder": "Numéro composé de 14 chiffres", "maxlength": "14"},
|
render_kw={"placeholder": "Numéro composé de 14 chiffres"},
|
||||||
)
|
)
|
||||||
nom_entreprise = _build_string_field("Nom de l'entreprise (*)")
|
nom_entreprise = _build_string_field("Nom de l'entreprise (*)")
|
||||||
adresse = _build_string_field("Adresse de l'entreprise (*)")
|
adresse = _build_string_field("Adresse de l'entreprise (*)")
|
||||||
@ -80,15 +80,6 @@ class EntrepriseCreationForm(FlaskForm):
|
|||||||
ville = _build_string_field("Ville de l'entreprise (*)")
|
ville = _build_string_field("Ville de l'entreprise (*)")
|
||||||
pays = _build_string_field("Pays de l'entreprise", required=False)
|
pays = _build_string_field("Pays de l'entreprise", required=False)
|
||||||
|
|
||||||
nom_correspondant = _build_string_field("Nom du correspondant (*)")
|
|
||||||
prenom_correspondant = _build_string_field("Prénom du correspondant (*)")
|
|
||||||
telephone = _build_string_field("Téléphone du correspondant (*)", required=False)
|
|
||||||
mail = StringField(
|
|
||||||
"Mail du correspondant (*)",
|
|
||||||
validators=[Optional(), Email(message="Adresse e-mail invalide")],
|
|
||||||
)
|
|
||||||
poste = _build_string_field("Poste du correspondant", required=False)
|
|
||||||
service = _build_string_field("Service du correspondant", required=False)
|
|
||||||
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
submit = SubmitField("Envoyer", render_kw=SUBMIT_MARGE)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
@ -96,29 +87,23 @@ class EntrepriseCreationForm(FlaskForm):
|
|||||||
if not FlaskForm.validate(self):
|
if not FlaskForm.validate(self):
|
||||||
validate = False
|
validate = False
|
||||||
|
|
||||||
if not self.telephone.data and not self.mail.data:
|
|
||||||
self.telephone.errors.append(
|
|
||||||
"Saisir un moyen de contact (mail ou téléphone)"
|
|
||||||
)
|
|
||||||
self.mail.errors.append("Saisir un moyen de contact (mail ou téléphone)")
|
|
||||||
validate = False
|
|
||||||
|
|
||||||
return validate
|
return validate
|
||||||
|
|
||||||
def validate_siret(self, siret):
|
def validate_siret(self, siret):
|
||||||
if EntreprisePreferences.get_check_siret():
|
if EntreprisePreferences.get_check_siret():
|
||||||
siret = siret.data.strip()
|
siret_data = siret.data.replace(" ", "")
|
||||||
if re.match("^\d{14}$", siret) is None:
|
self.siret.data = siret_data
|
||||||
|
if re.match("^\d{14}$", siret_data) is None:
|
||||||
raise ValidationError("Format incorrect")
|
raise ValidationError("Format incorrect")
|
||||||
try:
|
try:
|
||||||
req = requests.get(
|
req = requests.get(
|
||||||
f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret}"
|
f"https://entreprise.data.gouv.fr/api/sirene/v1/siret/{siret_data}"
|
||||||
)
|
)
|
||||||
if req.status_code != 200:
|
if req.status_code != 200:
|
||||||
raise ValidationError("SIRET inexistant")
|
raise ValidationError("SIRET inexistant")
|
||||||
except requests.ConnectionError:
|
except requests.ConnectionError:
|
||||||
print("no internet")
|
print("no internet")
|
||||||
entreprise = Entreprise.query.filter_by(siret=siret).first()
|
entreprise = Entreprise.query.filter_by(siret=siret_data).first()
|
||||||
if entreprise is not None:
|
if entreprise is not None:
|
||||||
lien = f'<a href="/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}">ici</a>'
|
lien = f'<a href="/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}">ici</a>'
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
|
@ -295,23 +295,12 @@ def add_entreprise():
|
|||||||
)
|
)
|
||||||
db.session.add(entreprise)
|
db.session.add(entreprise)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
db.session.refresh(entreprise)
|
|
||||||
correspondant = EntrepriseCorrespondant(
|
|
||||||
entreprise_id=entreprise.id,
|
|
||||||
nom=form.nom_correspondant.data.strip(),
|
|
||||||
prenom=form.prenom_correspondant.data.strip(),
|
|
||||||
telephone=form.telephone.data.strip(),
|
|
||||||
mail=form.mail.data.strip(),
|
|
||||||
poste=form.poste.data.strip(),
|
|
||||||
service=form.service.data.strip(),
|
|
||||||
)
|
|
||||||
db.session.add(correspondant)
|
|
||||||
if current_user.has_permission(Permission.RelationsEntreprisesValidate, None):
|
if current_user.has_permission(Permission.RelationsEntreprisesValidate, None):
|
||||||
entreprise.visible = True
|
entreprise.visible = True
|
||||||
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
|
nom_entreprise = f"<a href=/ScoDoc/entreprises/fiche_entreprise/{entreprise.id}>{entreprise.nom}</a>"
|
||||||
log = EntrepriseLog(
|
log = EntrepriseLog(
|
||||||
authenticated_user=current_user.user_name,
|
authenticated_user=current_user.user_name,
|
||||||
text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom}) avec un correspondant",
|
text=f"{nom_entreprise} - Création de la fiche entreprise ({entreprise.nom})",
|
||||||
)
|
)
|
||||||
db.session.add(log)
|
db.session.add(log)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
@ -322,7 +311,7 @@ def add_entreprise():
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
if EntreprisePreferences.get_email_notifications():
|
if EntreprisePreferences.get_email_notifications():
|
||||||
are.send_email_notifications_entreprise(
|
are.send_email_notifications_entreprise(
|
||||||
"entreprise en attente de validation", entreprise, correspondant
|
"entreprise en attente de validation", entreprise
|
||||||
)
|
)
|
||||||
flash("L'entreprise a été ajouté à la liste pour la validation.")
|
flash("L'entreprise a été ajouté à la liste pour la validation.")
|
||||||
return redirect(url_for("entreprises.index"))
|
return redirect(url_for("entreprises.index"))
|
||||||
@ -759,29 +748,18 @@ def delete_correspondant(id):
|
|||||||
)
|
)
|
||||||
form = SuppressionConfirmationForm()
|
form = SuppressionConfirmationForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
correspondant_count = EntrepriseCorrespondant.query.filter_by(
|
db.session.delete(correspondant)
|
||||||
entreprise_id=correspondant.entreprise_id
|
log = EntrepriseLog(
|
||||||
).count()
|
authenticated_user=current_user.user_name,
|
||||||
if correspondant_count == 1:
|
object=correspondant.entreprise_id,
|
||||||
flash(
|
text="Suppression d'un correspondant",
|
||||||
"Le correspondant n'a pas été supprimé de la fiche entreprise. (1 correspondant minimum)"
|
)
|
||||||
)
|
db.session.add(log)
|
||||||
return redirect(
|
db.session.commit()
|
||||||
url_for("entreprises.fiche_entreprise", id=correspondant.entreprise_id)
|
flash("Le correspondant a été supprimé de la fiche entreprise.")
|
||||||
)
|
return redirect(
|
||||||
else:
|
url_for("entreprises.fiche_entreprise", id=correspondant.entreprise_id)
|
||||||
db.session.delete(correspondant)
|
)
|
||||||
log = EntrepriseLog(
|
|
||||||
authenticated_user=current_user.user_name,
|
|
||||||
object=correspondant.entreprise_id,
|
|
||||||
text="Suppression d'un correspondant",
|
|
||||||
)
|
|
||||||
db.session.add(log)
|
|
||||||
db.session.commit()
|
|
||||||
flash("Le correspondant a été supprimé de la fiche entreprise.")
|
|
||||||
return redirect(
|
|
||||||
url_for("entreprises.fiche_entreprise", id=correspondant.entreprise_id)
|
|
||||||
)
|
|
||||||
return render_template(
|
return render_template(
|
||||||
"entreprises/delete_confirmation.html",
|
"entreprises/delete_confirmation.html",
|
||||||
title="Supression correspondant",
|
title="Supression correspondant",
|
||||||
@ -842,7 +820,11 @@ def edit_contact(id):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/fiche_entreprise/contacts/<int:id>")
|
@bp.route("/fiche_entreprise/contacts/<int:id>")
|
||||||
|
@permission_required(Permission.RelationsEntreprisesView)
|
||||||
def contacts(id):
|
def contacts(id):
|
||||||
|
"""
|
||||||
|
Permet d'afficher une page avec la liste des contacts d'une entreprise
|
||||||
|
"""
|
||||||
contacts = EntrepriseContact.query.filter_by(entreprise=id).all()
|
contacts = EntrepriseContact.query.filter_by(entreprise=id).all()
|
||||||
return render_template(
|
return render_template(
|
||||||
"entreprises/contacts.html",
|
"entreprises/contacts.html",
|
||||||
@ -955,6 +937,7 @@ def edit_stage_apprentissage(id):
|
|||||||
|
|
||||||
|
|
||||||
@bp.route("/delete_stage_apprentissage/<int:id>", methods=["GET", "POST"])
|
@bp.route("/delete_stage_apprentissage/<int:id>", methods=["GET", "POST"])
|
||||||
|
@permission_required(Permission.RelationsEntreprisesChange)
|
||||||
def delete_stage_apprentissage(id):
|
def delete_stage_apprentissage(id):
|
||||||
"""
|
"""
|
||||||
Permet de supprimer un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
|
Permet de supprimer un étudiant ayant réalisé un stage ou une alternance sur la fiche entreprise de l'entreprise
|
||||||
@ -1083,7 +1066,8 @@ def export_entreprises():
|
|||||||
filename = title
|
filename = title
|
||||||
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
flash("Aucune entreprise dans la base.")
|
||||||
|
return redirect(url_for("entreprises.index"))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/get_import_entreprises_file_sample")
|
@bp.route("/get_import_entreprises_file_sample")
|
||||||
@ -1141,7 +1125,7 @@ def import_entreprises():
|
|||||||
):
|
):
|
||||||
siret_list.append(entreprise_data[0])
|
siret_list.append(entreprise_data[0])
|
||||||
entreprise = Entreprise(
|
entreprise = Entreprise(
|
||||||
siret=entreprise_data[0],
|
siret=entreprise_data[0].replace(" ", ""),
|
||||||
nom=entreprise_data[1],
|
nom=entreprise_data[1],
|
||||||
adresse=entreprise_data[2],
|
adresse=entreprise_data[2],
|
||||||
ville=entreprise_data[3],
|
ville=entreprise_data[3],
|
||||||
@ -1217,7 +1201,8 @@ def export_correspondants():
|
|||||||
filename = title
|
filename = title
|
||||||
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
return scu.send_file(xlsx, filename, scu.XLSX_SUFFIX, scu.XLSX_MIMETYPE)
|
||||||
else:
|
else:
|
||||||
abort(404)
|
flash("Aucun correspondant dans la base.")
|
||||||
|
return redirect(url_for("entreprises.correspondants"))
|
||||||
|
|
||||||
|
|
||||||
@bp.route("/get_import_correspondants_file_sample")
|
@bp.route("/get_import_correspondants_file_sample")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{% import 'bootstrap/wtf.html' as wtf %}
|
{% import 'bootstrap/wtf.html' as wtf %}
|
||||||
|
|
||||||
{% block app_content %}
|
{% block app_content %}
|
||||||
<h1>Ajout entreprise avec correspondant</h1>
|
<h1>Ajout entreprise</h1>
|
||||||
<br>
|
<br>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
@ -16,45 +16,36 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.onload = function(e){
|
document.getElementById("siret").addEventListener("keyup", autocomplete);
|
||||||
document.getElementById("siret").addEventListener("keyup", autocomplete);
|
|
||||||
|
|
||||||
function autocomplete() {
|
function autocomplete() {
|
||||||
var input = document.getElementById("siret").value;
|
var input = document.getElementById("siret").value.replaceAll(" ", "")
|
||||||
if(input.length == 14) {
|
if(input.length >= 14) {
|
||||||
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input)
|
fetch("https://entreprise.data.gouv.fr/api/sirene/v1/siret/" + input)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
if(response.ok)
|
if(response.ok)
|
||||||
return response.json()
|
return response.json()
|
||||||
else {
|
else {
|
||||||
emptyForm()
|
emptyForm()
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => fillForm(response))
|
|
||||||
.catch(err => err)
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
.then(response => fillForm(response))
|
||||||
function fillForm(response) {
|
.catch(err => err)
|
||||||
document.getElementById("nom_entreprise").value = response.etablissement.l1_normalisee
|
|
||||||
document.getElementById("adresse").value = response.etablissement.l4_normalisee
|
|
||||||
document.getElementById("codepostal").value = response.etablissement.code_postal
|
|
||||||
document.getElementById("ville").value = response.etablissement.libelle_commune
|
|
||||||
}
|
|
||||||
|
|
||||||
function emptyForm() {
|
|
||||||
document.getElementById("nom_entreprise").value = ''
|
|
||||||
document.getElementById("adresse").value = ''
|
|
||||||
document.getElementById("codepostal").value = ''
|
|
||||||
document.getElementById("ville").value = ''
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{# ajout margin-bottom sur le champ pays #}
|
function fillForm(response) {
|
||||||
var champ_pays = document.getElementById("pays")
|
document.getElementById("nom_entreprise").value = response.etablissement.l1_normalisee
|
||||||
if (champ_pays !== null) {
|
document.getElementById("adresse").value = response.etablissement.l4_normalisee
|
||||||
var closest_form_group = champ_pays.closest(".form-group")
|
document.getElementById("codepostal").value = response.etablissement.code_postal
|
||||||
closest_form_group.style.marginBottom = "50px"
|
document.getElementById("ville").value = response.etablissement.libelle_commune
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyForm() {
|
||||||
|
document.getElementById("nom_entreprise").value = ''
|
||||||
|
document.getElementById("adresse").value = ''
|
||||||
|
document.getElementById("codepostal").value = ''
|
||||||
|
document.getElementById("ville").value = ''
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -95,7 +95,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td>{{ data[0].date_debut.strftime('%d/%m/%Y') }}</td>
|
<td>{{ data[0].date_debut.strftime('%d/%m/%Y') }}</td>
|
||||||
<td>{{ data[0].date_fin.strftime('%d/%m/%Y') }}</td>
|
<td>{{ data[0].date_fin.strftime('%d/%m/%Y') }}</td>
|
||||||
<td>{{(data[0].date_fin-data[0].date_debut).days//7}} semaines</td>
|
<td>{{ (data[0].date_fin-data[0].date_debut).days//7 }} semaines</td>
|
||||||
<td>{{ data[0].type_offre }}</td>
|
<td>{{ data[0].type_offre }}</td>
|
||||||
<td>{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}</td>
|
<td>{{ data[1].nom|format_nom }} {{ data[1].prenom|format_prenom }}</td>
|
||||||
<td>{% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %}</td>
|
<td>{% if data[0].formation_text %}{{ data[0].formation_text }}{% endif %}</td>
|
||||||
@ -118,13 +118,13 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td data-priority="">Date début</td>
|
<td>Date début</td>
|
||||||
<td data-priority="">Date fin</td>
|
<td>Date fin</td>
|
||||||
<td data-priority="">Durée</td>
|
<td>Durée</td>
|
||||||
<td data-priority="">Type</td>
|
<td>Type</td>
|
||||||
<td data-priority="">Étudiant</td>
|
<td>Étudiant</td>
|
||||||
<td data-priority="">Formation</td>
|
<td>Formation</td>
|
||||||
<td data-priority="">Notes</td>
|
<td>Notes</td>
|
||||||
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
|
{% if current_user.has_permission(current_user.Permission.RelationsEntreprisesChange, None) %}
|
||||||
<td>Action</td>
|
<td>Action</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -16,32 +16,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if correspondants %}
|
|
||||||
<div>
|
|
||||||
{% for correspondant in correspondants %}
|
|
||||||
<div>
|
|
||||||
<h3>Correspondant</h3>
|
|
||||||
<div class="correspondant">
|
|
||||||
Nom : {{ correspondant.nom }}<br>
|
|
||||||
Prénom : {{ correspondant.prenom }}<br>
|
|
||||||
{% if correspondant.telephone %}
|
|
||||||
Téléphone : {{ correspondant.telephone }}<br>
|
|
||||||
{% endif %}
|
|
||||||
{% if correspondant.mail %}
|
|
||||||
Mail : {{ correspondant.mail }}<br>
|
|
||||||
{% endif %}
|
|
||||||
{% if correspondant.poste %}
|
|
||||||
Poste : {{ correspondant.poste }}<br>
|
|
||||||
{% endif %}
|
|
||||||
{% if correspondant.service %}
|
|
||||||
Service : {{ correspondant.service }}<br>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<a class="btn btn-success" href="{{ url_for('entreprises.validate_entreprise', id=entreprise.id) }}">Valider</a>
|
<a class="btn btn-success" href="{{ url_for('entreprises.validate_entreprise', id=entreprise.id) }}">Valider</a>
|
||||||
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_validation_entreprise', id=entreprise.id) }}">Supprimer</a>
|
<a class="btn btn-danger" href="{{ url_for('entreprises.delete_validation_entreprise', id=entreprise.id) }}">Supprimer</a>
|
||||||
|
@ -25,5 +25,21 @@
|
|||||||
var closest_form_control = champ_depts.closest(".form-control")
|
var closest_form_control = champ_depts.closest(".form-control")
|
||||||
closest_form_control.classList.remove("form-control")
|
closest_form_control.classList.remove("form-control")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.getElementById("type_offre").addEventListener("change", expiration);
|
||||||
|
|
||||||
|
function expiration() {
|
||||||
|
var date = new Date()
|
||||||
|
var expiration = document.getElementById("expiration_date")
|
||||||
|
var type_offre = document.getElementById("type_offre").value
|
||||||
|
if (type_offre == "Alternance") {
|
||||||
|
expiration.value = `${date.getFullYear() + 1}-01-01`
|
||||||
|
} else {
|
||||||
|
if(date.getMonth() + 1 < 7)
|
||||||
|
expiration.value = `${date.getFullYear()}-07-01`
|
||||||
|
else
|
||||||
|
expiration.value = `${date.getFullYear() + 1}-07-01`
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user