Merge branch '2.3-maintenance'

This commit is contained in:
Adrian Moennich 2021-01-20 14:50:43 +01:00
commit b3abd4f104
5 changed files with 121 additions and 88 deletions

View File

@ -28,6 +28,8 @@
- Remove the "Assistant Zoom ID" logic due to problems with Zoom's API rate limits (all meetings created were counted against the assistant's rate limit instead of the host's); this means the host can no longer be changed, but Indico instead provides an option to event managers to make themselves a co-host.
- Fix an error when changing the linked object of a recurring Zoom room in Indico
- Include Zoom join links in the event's ical export (note: only Zoom meetings with a public passcode are displayed unless you have at least Indico v2.3.3)
- Skip deleted Zoom meetings when cloning events
- Mark Zoom meetings as deleted when receiving the corresponding webhook event
**Breaking change:** The email domains are now stored as a list of strings instead of a comma-separated list. You need to update them in the plugin settings.

View File

@ -15,7 +15,7 @@ from werkzeug.exceptions import Forbidden
from indico.core.db import db
from indico.modules.vc.controllers import RHVCSystemEventBase
from indico.modules.vc.exceptions import VCRoomError
from indico.modules.vc.models.vc_rooms import VCRoom
from indico.modules.vc.models.vc_rooms import VCRoom, VCRoomStatus
from indico.util.i18n import _
from indico.web.rh import RH
@ -49,8 +49,8 @@ class RHWebhook(RH):
raise Forbidden
@use_kwargs({
'event': fields.String(),
'payload': fields.Dict()
'event': fields.String(required=True),
'payload': fields.Dict(required=True)
})
def _process(self, event, payload):
meeting_id = payload['object']['id']
@ -61,7 +61,10 @@ class RHWebhook(RH):
current_plugin.logger.debug('Action for unhandled Zoom room: %s', meeting_id)
return
if event in {'meeting.updated', 'webinar.updated', 'meeting.deleted', 'webinar.deleted'}:
if event in ('meeting.updated', 'webinar.updated'):
current_plugin.refresh_room(vc_room, None)
elif event in ('meeting.deleted', 'webinar.deleted'):
current_plugin.logger.info('Zoom room deleted: %s', meeting_id)
vc_room.status = VCRoomStatus.deleted
else:
current_plugin.logger.warning('Unhandled Zoom webhook payload: %s', event)

View File

@ -16,11 +16,12 @@ from wtforms.validators import DataRequired, ValidationError
from indico.core import signals
from indico.core.auth import multipass
from indico.core.errors import UserValueError
from indico.core.plugins import IndicoPlugin, render_plugin_template, url_for_plugin
from indico.modules.events.views import WPSimpleEventDisplay
from indico.modules.vc import VCPluginMixin, VCPluginSettingsFormBase
from indico.modules.vc.exceptions import VCRoomError
from indico.modules.vc.models.vc_rooms import VCRoom
from indico.modules.vc.exceptions import VCRoomError, VCRoomNotFoundError
from indico.modules.vc.models.vc_rooms import VCRoom, VCRoomStatus
from indico.modules.vc.views import WPVCEventPage, WPVCManageEvent
from indico.util.user import principal_from_identifier
from indico.web.forms.fields import IndicoEnumSelectField, IndicoPasswordField, TextListField
@ -205,38 +206,41 @@ class ZoomPlugin(VCPluginMixin, IndicoPlugin):
super().update_data_association(event, vc_room, room_assoc, data)
if vc_room.data:
# this is not a new room
if association_is_new:
# this means we are updating an existing meeting with a new vc_room-event association
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
elif room_assoc.link_object != old_link:
# the booking should now be linked to something else
new_schedule_args = get_schedule_args(room_assoc.link_object) if room_assoc.link_object.start_dt else {}
meeting = fetch_zoom_meeting(vc_room)
current_schedule_args = {k: meeting[k] for k in {'start_time', 'duration'} if k in meeting}
try:
# this is not a new room
if association_is_new:
# this means we are updating an existing meeting with a new vc_room-event association
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
elif room_assoc.link_object != old_link:
# the booking should now be linked to something else
new_schedule_args = get_schedule_args(room_assoc.link_object) if room_assoc.link_object.start_dt else {}
meeting = fetch_zoom_meeting(vc_room)
current_schedule_args = {k: meeting[k] for k in {'start_time', 'duration'} if k in meeting}
# check whether the start time / duration of the scheduled meeting differs
if new_schedule_args != current_schedule_args:
if new_schedule_args:
update_zoom_meeting(vc_room.data['zoom_id'], new_schedule_args)
else:
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
# check whether the start time / duration of the scheduled meeting differs
if new_schedule_args != current_schedule_args:
if new_schedule_args:
update_zoom_meeting(vc_room.data['zoom_id'], new_schedule_args)
else:
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
except VCRoomNotFoundError as exc:
raise UserValueError(str(exc)) from exc
room_assoc.data['password_visibility'] = data.pop('password_visibility')
flag_modified(room_assoc, 'data')
@ -422,20 +426,31 @@ class ZoomPlugin(VCPluginMixin, IndicoPlugin):
vc_room = old_event_vc_room.vc_room
is_webinar = vc_room.data.get('meeting_type', 'regular') == 'webinar'
has_only_one_association = len({assoc.event_id for assoc in vc_room.events}) == 1
new_assoc = super().clone_room(old_event_vc_room, link_object)
if has_only_one_association:
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
return new_assoc
try:
update_zoom_meeting(vc_room.data['zoom_id'], {
'start_time': None,
'duration': None,
'type': (
ZoomMeetingType.recurring_webinar_no_time
if is_webinar
else ZoomMeetingType.recurring_meeting_no_time
)
})
except VCRoomNotFoundError:
# this check is needed in order to avoid multiple flashes
if vc_room.status != VCRoomStatus.deleted:
# mark room as deleted
vc_room.status = VCRoomStatus.deleted
flash(
_('The room "{}" no longer exists in Zoom and was removed from the event').format(vc_room.name),
'warning'
)
# no need to create an association to a room marked as deleted
return None
# return the new association
return super().clone_room(old_event_vc_room, link_object)
def get_blueprints(self):
return blueprint

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2021-01-11 10:36+0100\n"
"POT-Creation-Date: 2021-01-20 13:54+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -90,35 +90,35 @@ msgstr ""
msgid "Meeting passcode (8-10 digits)"
msgstr ""
#: indico_vc_zoom/forms.py:72 indico_vc_zoom/plugin.py:86
#: indico_vc_zoom/forms.py:72 indico_vc_zoom/plugin.py:87
msgid "Mute audio"
msgstr ""
#: indico_vc_zoom/forms.py:74 indico_vc_zoom/plugin.py:88
#: indico_vc_zoom/forms.py:74 indico_vc_zoom/plugin.py:89
msgid "Participants will join the VC room muted by default "
msgstr ""
#: indico_vc_zoom/forms.py:76 indico_vc_zoom/plugin.py:90
#: indico_vc_zoom/forms.py:76 indico_vc_zoom/plugin.py:91
msgid "Mute video (host)"
msgstr ""
#: indico_vc_zoom/forms.py:78 indico_vc_zoom/plugin.py:92
#: indico_vc_zoom/forms.py:78 indico_vc_zoom/plugin.py:93
msgid "The host will join the VC room with video disabled"
msgstr ""
#: indico_vc_zoom/forms.py:80 indico_vc_zoom/plugin.py:94
#: indico_vc_zoom/forms.py:80 indico_vc_zoom/plugin.py:95
msgid "Mute video (participants)"
msgstr ""
#: indico_vc_zoom/forms.py:82 indico_vc_zoom/plugin.py:96
#: indico_vc_zoom/forms.py:82 indico_vc_zoom/plugin.py:97
msgid "Participants will join the VC room with video disabled"
msgstr ""
#: indico_vc_zoom/forms.py:84 indico_vc_zoom/plugin.py:103
#: indico_vc_zoom/forms.py:84 indico_vc_zoom/plugin.py:104
msgid "Waiting room"
msgstr ""
#: indico_vc_zoom/forms.py:86 indico_vc_zoom/plugin.py:105
#: indico_vc_zoom/forms.py:86 indico_vc_zoom/plugin.py:106
msgid "Participants may be kept in a waiting room by the host"
msgstr ""
@ -135,48 +135,48 @@ msgstr ""
msgid "This user has no Zoom account"
msgstr ""
#: indico_vc_zoom/plugin.py:53
#: indico_vc_zoom/plugin.py:54
msgid "API Key"
msgstr ""
#: indico_vc_zoom/plugin.py:55
#: indico_vc_zoom/plugin.py:56
msgid "API Secret"
msgstr ""
#: indico_vc_zoom/plugin.py:57
#: indico_vc_zoom/plugin.py:58
msgid "Webhook Token"
msgstr ""
#: indico_vc_zoom/plugin.py:58
#: indico_vc_zoom/plugin.py:59
msgid "Specify Zoom's webhook token if you want live updates"
msgstr ""
#: indico_vc_zoom/plugin.py:60
#: indico_vc_zoom/plugin.py:61
msgid "User lookup mode"
msgstr ""
#: indico_vc_zoom/plugin.py:61
#: indico_vc_zoom/plugin.py:62
msgid ""
"Specify how Indico should look up the zoom user that corresponds to an "
"Indico user."
msgstr ""
#: indico_vc_zoom/plugin.py:64
#: indico_vc_zoom/plugin.py:65
msgid "E-mail domains"
msgstr ""
#: indico_vc_zoom/plugin.py:66
#: indico_vc_zoom/plugin.py:67
msgid ""
"List of e-mail domains which can use the Zoom API. Indico attempts to "
"find Zoom accounts using all email addresses of a user which use those "
"domains."
msgstr ""
#: indico_vc_zoom/plugin.py:70
#: indico_vc_zoom/plugin.py:71
msgid "Indico identity providers"
msgstr ""
#: indico_vc_zoom/plugin.py:72
#: indico_vc_zoom/plugin.py:73
msgid ""
"Identity providers from which to get usernames. Indico queries those "
"providers using the email addresses of the user and attempts to find Zoom"
@ -184,87 +184,91 @@ msgid ""
"domain."
msgstr ""
#: indico_vc_zoom/plugin.py:77
#: indico_vc_zoom/plugin.py:78
msgid "Enterprise domain"
msgstr ""
#: indico_vc_zoom/plugin.py:79
#: indico_vc_zoom/plugin.py:80
msgid ""
"The domain name used together with the usernames from the Indico identity"
" provider"
msgstr ""
#: indico_vc_zoom/plugin.py:82
#: indico_vc_zoom/plugin.py:83
msgid "Allow Webinars (Experimental)"
msgstr ""
#: indico_vc_zoom/plugin.py:84
#: indico_vc_zoom/plugin.py:85
msgid "Allow webinars to be created through Indico. Use at your own risk."
msgstr ""
#: indico_vc_zoom/plugin.py:98
#: indico_vc_zoom/plugin.py:99
msgid "Join Before Host"
msgstr ""
#: indico_vc_zoom/plugin.py:100
#: indico_vc_zoom/plugin.py:101
msgid ""
"Allow participants to join the meeting before the host starts the "
"meeting. Only used for scheduled or recurring meetings."
msgstr ""
#: indico_vc_zoom/plugin.py:107
#: indico_vc_zoom/plugin.py:108
msgid "Creation email footer"
msgstr ""
#: indico_vc_zoom/plugin.py:108
#: indico_vc_zoom/plugin.py:109
msgid "Footer to append to emails sent upon creation of a VC room"
msgstr ""
#: indico_vc_zoom/plugin.py:110
#: indico_vc_zoom/plugin.py:111
msgid "Send host URL"
msgstr ""
#: indico_vc_zoom/plugin.py:112
#: indico_vc_zoom/plugin.py:113
msgid ""
"Whether to send an e-mail with the Host URL to the meeting host upon "
"creation of a meeting"
msgstr ""
#: indico_vc_zoom/plugin.py:118
#: indico_vc_zoom/plugin.py:119
msgid "Invalid identity providers: {}"
msgstr ""
#: indico_vc_zoom/plugin.py:319
#: indico_vc_zoom/plugin.py:335
msgid ""
"Could not create the room in Zoom. Please contact support if the error "
"persists"
msgstr ""
#: indico_vc_zoom/plugin.py:403
#: indico_vc_zoom/plugin.py:419
msgid "Room didn't existing in Zoom anymore"
msgstr ""
#: indico_vc_zoom/plugin.py:406
#: indico_vc_zoom/plugin.py:422
msgid "Zoom Error: \"{}\""
msgstr ""
#: indico_vc_zoom/plugin.py:409
#: indico_vc_zoom/plugin.py:425
msgid "Problem deleting room"
msgstr ""
#: indico_vc_zoom/plugin.py:490
#: indico_vc_zoom/plugin.py:449
msgid "The room \"{}\" no longer exists in Zoom and was removed from the event"
msgstr ""
#: indico_vc_zoom/plugin.py:517
msgid ""
"There are one or more scheduled Zoom meetings associated with this event "
"which were not automatically updated."
msgstr ""
#: indico_vc_zoom/plugin.py:493
#: indico_vc_zoom/plugin.py:520
msgid ""
"There are one or more scheduled Zoom meetings associated with the "
"contribution \"{}\" which were not automatically updated."
msgstr ""
#: indico_vc_zoom/plugin.py:496
#: indico_vc_zoom/plugin.py:523
msgid ""
"There are one or more scheduled Zoom meetings associated with this "
"session block which were not automatically updated."
@ -292,11 +296,15 @@ msgid ""
"persists."
msgstr ""
#: indico_vc_zoom/util.py:150
#: indico_vc_zoom/util.py:153
msgid "Room no longer exists in Zoom"
msgstr ""
#: indico_vc_zoom/util.py:155
msgid "Can't update meeting. Please contact support if the error persists."
msgstr ""
#: indico_vc_zoom/util.py:206
#: indico_vc_zoom/util.py:211
msgid "Could not find Zoom user for alternative host"
msgstr ""

View File

@ -145,6 +145,11 @@ def update_zoom_meeting(zoom_id, changes, is_webinar=False):
except HTTPError as e:
from indico_vc_zoom.plugin import ZoomPlugin
ZoomPlugin.logger.exception("Error updating meeting '%s': %s", zoom_id, e.response.content)
if e.response.json()['code'] == 3001:
# "Meeting does not exist"
raise VCRoomNotFoundError(_("Room no longer exists in Zoom"))
raise VCRoomError(_("Can't update meeting. Please contact support if the error persists."))