-
Notifications
You must be signed in to change notification settings - Fork 6
Transfer meeting ownership #391
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
e6f9f30
580eeac
7f6b506
83b362d
bda0d8a
ffaa9e9
79a0b4c
530208d
5a541d5
704d6fb
32428d7
19e583b
5f69374
646762b
2a01ed0
928fab0
e7c67c2
3fd08c8
ff3a258
5bb7271
af76d01
e51ed81
6db85c1
07e9016
4e561a2
6045da7
3ef3b4c
9e1c625
0f7eb76
e2bb218
6bea1f9
364f4f3
0773ef6
aee0c5d
7b5f713
7ccaccc
ba5e115
74ecec9
eabad4b
0941d57
f4d9d1f
3da3054
8023f50
83a59ed
b5711ef
1d1db1c
e27a45f
737c616
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,7 @@ | |
| from ..session import is_admin_mode | ||
| from ..session import meeting_access_required | ||
| from ..utils import send_delegation_mail | ||
| from ..utils import send_new_owner_mail | ||
|
|
||
| bp = Blueprint("meetings", __name__) | ||
|
|
||
|
|
@@ -479,3 +480,68 @@ def remove_delegate(meeting: Meeting, user: User, delegate: User): | |
| admin_mode=is_admin_mode() or None, | ||
| ) | ||
| ) | ||
|
|
||
|
|
||
| @bp.route("/meeting/transfert-meeting-ownership/<meeting:meeting>/<user:delegate>") | ||
| @check_oidc_connection(auth) | ||
| @auth.oidc_auth("default") | ||
| @meeting_access_required() | ||
| def transfert_meeting_ownership(meeting: Meeting, user: User, delegate: User): | ||
| """Display the page for manage meeting delegation.""" | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ça ne semble pas être la bonne docstring :) |
||
| new_owner = delegate | ||
| if new_owner is None or new_owner not in meeting.get_all_delegates: | ||
| abort(404) | ||
|
|
||
| previous_owner = meeting.owner | ||
| meeting.owner = new_owner | ||
| meeting.owner_id = new_owner.id | ||
| new_access = MeetingAccess( | ||
| meeting_id=meeting.id, | ||
| user_id=previous_owner.id, | ||
| level=AccessLevel.DELEGATE, | ||
| ) | ||
| removed_access = MeetingAccess.query.filter_by( | ||
| user_id=new_owner.id, meeting_id=meeting.id | ||
| ).one() | ||
| db.session.add(new_access) | ||
| db.session.delete(removed_access) | ||
| db.session.commit() | ||
| current_app.logger.info( | ||
| "Meeting %s %s have a new owner : %s %s", | ||
| meeting.id, | ||
| meeting.name, | ||
| new_owner.id, | ||
| new_owner.fullname, | ||
| ) | ||
| current_app.logger.info( | ||
| "%s became delegate of meeting %s %s", | ||
| previous_owner.email, | ||
| meeting.id, | ||
| meeting.name, | ||
| ) | ||
| current_app.logger.info( | ||
| "%s removed from delegates of meeting %s %s", | ||
| new_owner.email, | ||
| meeting.id, | ||
| meeting.name, | ||
| ) | ||
|
Comment on lines
+491
to
+527
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. J'extrairais ça dans une méthode dédiée pour alléger la vue. |
||
| flash( | ||
| _( | ||
| "%(owner_name)s est le nouveau propriétaire de %(meeting_name)s", | ||
| owner_name=new_owner.fullname, | ||
| meeting_name=meeting.name, | ||
| ), | ||
| "success", | ||
| ) | ||
| send_delegation_mail(meeting, previous_owner, new_delegation=True) | ||
| send_new_owner_mail(meeting, new_owner, previous_owner) | ||
| return ( | ||
| redirect(url_for("public.welcome")) | ||
| if not is_admin_mode() | ||
| else redirect( | ||
| url_for( | ||
| "admin.meeting_infos", | ||
| meeting=meeting, | ||
| ) | ||
| ) | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -294,6 +294,17 @@ class MeetingSearchForm(FlaskForm): | |
| ) | ||
|
|
||
|
|
||
| class TransferMeetingOwnership(FlaskForm): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cette classe n'est jamais appelée non ? |
||
| select = SelectField( | ||
| label=_("Sélectionner un délégataire"), | ||
| description=_( | ||
| "Transférer la propriété d'une réunion est action définitive. Vous deviendrez délégataire de cette réunion." | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. est une action définitive |
||
| ), | ||
| validators=[validators.DataRequired()], | ||
| choices=[], | ||
| ) | ||
|
|
||
|
|
||
| class GroupSearchForm(FlaskForm): | ||
| search = StringField( | ||
| label=_("Rechercher un groupe"), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -233,6 +233,7 @@ def get_all_delegates(self): | |
| MeetingAccess.meeting_id == self.id, | ||
| MeetingAccess.level == AccessLevel.DELEGATE, | ||
| ) | ||
| .order_by(User.family_name, User.given_name) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C'est un peu hors PR, tu peux pousser des choses comme ça directement sur main :) (mais ce n'est pas grave) |
||
| ).all() | ||
|
|
||
| def url_for_role(self, role): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -29,3 +29,8 @@ | |
| {% endif %} | ||
| {% include 'meeting/delegation-management.html' %} | ||
| {% endblock %} | ||
|
|
||
| {# | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C'est du code commenté. |
||
| fr-icon-arrow-right-s-line-double | ||
| fr-icon-share-forward-line | ||
| #} | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| <html> | ||
| {% trans %}Bonjour{% endtrans %} {{ new_owner.given_name }} {{ new_owner.family_name }}, | ||
| <br/> | ||
| <br/> | ||
| {{ previous_owner.fullname }} {% trans %}vous a transféré la propriété de la réunion{% endtrans %} {{ meeting.name }}. | ||
| <br/> | ||
| <br/> | ||
| {% trans %}Vous pouvez dès à présent consulter et gérer les délégataires, l'édition, les enregistrements et les fichiers associés à cette réunion.{% endtrans %} | ||
| <br/> | ||
| <br/> | ||
| {% trans %}Rendez vous simplement sur <a href="{{ welcome_url }}">votre espace</a>.{% endtrans %} | ||
| </html> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| {% trans %}Bonjour{% endtrans %} {{ new_owner.given_name }} {{ new_owner.family_name }}, | ||
|
|
||
| {{ previous_owner.fullname }} {% trans %}vous a transféré la propriété de la réunion{% endtrans %} {{ meeting.name }}. | ||
|
|
||
| {% trans %}Vous pouvez dès à présent consulter et gérer les délégataires, l'édition, les enregistrements et les fichiers associés à cette réunion.{% endtrans %} | ||
|
|
||
| {% trans %}Rendez-vous simplement sur votre espace :{% endtrans %} {{ welcome_url }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| <dialog id="transfert-ownership-confirmation-{{ delegate.id }}" class="fr-modal" role="dialog" aria-labelledby="transfert-ownership-confirmation-{{ delegate.id }}-title"> | ||
| <div class="fr-container fr-container--fluid fr-container-md"> | ||
| <div class="fr-grid-row fr-grid-row--center"> | ||
| <div class="fr-col-12 fr-col-md-8 fr-col-lg-6"> | ||
| <div class="fr-modal__body"> | ||
| <div class="fr-modal__header"> | ||
| <button class="fr-btn--close fr-btn" aria-controls="transfert-ownership-confirmation-{{ delegate.id }}" title="{% trans %}Fermer{% endtrans %}"> | ||
| {% trans %}Fermer{% endtrans %} | ||
| </button> | ||
| </div> | ||
| <div class="fr-modal__content"> | ||
| <h1 id="transfert-ownership-confirmation-{{ delegate.id }}-title" class="fr-modal__title"> | ||
| {% trans meeting_name=meeting.name %}Transférer "{{ meeting_name }}" ?{% endtrans %} | ||
| </h1> | ||
| <p>{% trans meeting_name=meeting.name, delegate_name=delegate.fullname %}Voulez-vous vraiment transférer la propriété de "{{ meeting_name }}" à {{ delegate_name }} ?{% endtrans %}</p> | ||
| <div class="fr-notice fr-notice--info"> | ||
| <div class="fr-container"> | ||
| <div class="fr-notice__body"> | ||
| <p class="fr-notice__title">{% trans %}La confirmation est définitive.{% endtrans %}</p> | ||
| </div> | ||
| <p>{% trans %}Vous deviendrez délégataire de cette réunion.{% endtrans %}</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="fr-modal__footer"> | ||
| <ul class="fr-btns-group fr-btns-group--inline-reverse fr-btns-group--inline-lg fr-btns-group--right"> | ||
| <li> | ||
| <a class="fr-btn fr-btn--delete" href="{{ url_for("meetings.transfert_meeting_ownership", meeting=meeting, delegate=delegate, admin_mode=admin_mode or None) }}"> | ||
| {% trans %}Confirmer{% endtrans %} | ||
| </a> | ||
| </li> | ||
| <li> | ||
| <button type="button" class="fr-btn fr-btn--secondary" aria-controls="transfert-ownership-confirmation-{{ delegate.id }}" title="{% trans %}Annuler{% endtrans %}">{% trans %}Annuler{% endtrans %}</button> | ||
| </li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </dialog> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,6 +91,26 @@ def send_delegation_mail(meeting, delegate, new_delegation: bool): | |
| send_email(msg, text, html, smtp) | ||
|
|
||
|
|
||
| def send_new_owner_mail(meeting, new_owner, previous_owner): | ||
| """Send email to inform the new meeting owner.""" | ||
| smtp = make_smtp() | ||
| msg = EmailMessage() | ||
| body_file = "mail_new_owner_body" | ||
| context = { | ||
| "meeting": meeting, | ||
| "new_owner": new_owner, | ||
| "previous_owner": previous_owner, | ||
| "welcome_url": url_for("public.welcome", _external=True), | ||
| } | ||
| text = render_template(f"meeting/mailto/{body_file}.txt", **context) | ||
| html = render_template(f"meeting/mailto/{body_file}.html", **context) | ||
| msg["Subject"] = str(_(f"Transfert de {meeting.name}")) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pas de f-string dans |
||
| msg["From"] = smtp["from_email"] | ||
| msg["To"] = new_owner.email | ||
|
|
||
| send_email(msg, text, html, smtp) | ||
|
|
||
|
|
||
| def _build_recording_links(playbacks): | ||
| """Return an ordered list of {label, url} entries from a BBB playbacks dict.""" | ||
| links = [] | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| from b3desk.models import db | ||
| from b3desk.models.meetings import AccessLevel | ||
| from b3desk.models.meetings import MeetingAccess | ||
|
|
||
|
|
||
| def test_owner_can_transfer_ownership_to_delegate( | ||
| client_app, authenticated_user, user_2, meeting, user, smtpd, caplog | ||
| ): | ||
| """Test owner can transfer ownership to delegate.""" | ||
| new_access = MeetingAccess( | ||
| meeting_id=meeting.id, | ||
| user_id=user_2.id, | ||
| level=AccessLevel.DELEGATE, | ||
| ) | ||
| db.session.add(new_access) | ||
| db.session.commit() | ||
| client_app.get("/meeting/transfert-meeting-ownership/1/2", status=302) | ||
| assert meeting.owner is user_2 | ||
| assert user in meeting.get_all_delegates | ||
| assert len(smtpd.messages) == 2 | ||
| assert "Meeting 1 meeting have a new owner : 2 Berenice Cooler" in caplog.text | ||
| assert "alice@domain.tld became delegate of meeting 1 meeting" in caplog.text | ||
| assert ( | ||
| "berenice@domain.tld removed from delegates of meeting 1 meeting" in caplog.text | ||
| ) | ||
| assert "Email sent to alice@domain.tld" in caplog.text | ||
| assert "Email sent to berenice@domain.tld" in caplog.text | ||
|
|
||
|
|
||
| def test_new_owner_is_not_delegate_display_404( | ||
| client_app, authenticated_user, meeting, user_2 | ||
| ): | ||
| """Test form displays message if form is empty.""" | ||
| client_app.get("/meeting/transfert-meeting-ownership/1/2", status=404) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
À la réflexion, les changements d'état devraient plutôt être derrière des requêtes POST.