Skip to content
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

Zoom Rooms - improvements in base Zoom plugin #236

Merged
merged 15 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[mypy]
disable_error_code = import-untyped
4 changes: 2 additions & 2 deletions vc_dummy/indico_vc_dummy/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ class DummyPlugin(VCPluginMixin, IndicoPlugin):

@property
def logo_url(self):
return url_for_plugin(self.name + '.static', filename='images/dummy_logo.png')
return url_for_plugin(self.name + '.static', filename='images/dummy_logo.svg')

@property
def icon_url(self):
return url_for_plugin(self.name + '.static', filename='images/dummy_icon.png')
return url_for_plugin(self.name + '.static', filename='images/dummy_icon.svg')

def get_blueprints(self):
return IndicoPluginBlueprint('vc_dummy', __name__)
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions vc_dummy/indico_vc_dummy/static/images/dummy_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
1 change: 1 addition & 0 deletions vc_dummy/indico_vc_dummy/static/images/dummy_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions vc_zoom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

## Changelog

### 3.3.1

- Adapt to Indico 3.3.5 changes
- Modernize UI elements

### 3.3

- Adapt to Indico 3.3 changes
Expand Down
2 changes: 1 addition & 1 deletion vc_zoom/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
# them and/or modify them under the terms of the MIT License;
# see the LICENSE file for more details.

pytest_plugins = 'indico'
pytest_plugins = ('indico', 'indico_vc_zoom.fixtures')
13 changes: 13 additions & 0 deletions vc_zoom/indico_vc_zoom/client/JoinButton.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file is part of the Indico plugins.
// Copyright (C) 2020 - 2024 CERN and ENEA
//
// The Indico plugins are free software; you can redistribute
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.

.button-label:global(.ui.image.label) {
> img {
margin: 0;
height: 1.3em !important;
}
}
151 changes: 151 additions & 0 deletions vc_zoom/indico_vc_zoom/client/JoinButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// This file is part of the Indico plugins.
// Copyright (C) 2020 - 2024 CERN and ENEA
//
// The Indico plugins are free software; you can redistribute
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.

import staticURL from 'indico-url:plugin_vc_zoom.static';

import React, {useState} from 'react';
import {Button, ButtonGroup, Confirm, Dropdown, Icon, Label, Loader, Popup, SemanticICONS} from 'semantic-ui-react';

import {Translate} from 'indico/react/i18n';
import {handleAxiosError, indicoAxios} from 'indico/utils/axios';

import './JoinButton.module.scss';

interface OptionsButtonProps {
url: string;
onMadeAltHost: () => void;
}

/** A dropdown button which shows additional actions, such as the possibility to take over as meeting co-host */
function OptionsButton({url, onMadeAltHost}: OptionsButtonProps) {
const [isConfirmOpen, setConfirmOpen] = useState(false);
const [state, setState] = useState('idle');

async function makeAlternativeHost() {
setConfirmOpen(false);
try {
setState('submitting');
await indicoAxios.post(url);
pferreir marked this conversation as resolved.
Show resolved Hide resolved
setState('success');
} catch (error) {
handleAxiosError(error);
}
setTimeout(() => {
setState('idle');
onMadeAltHost();
}, 3000);
}

const icons = new Map<string, SemanticICONS>([
['idle', 'cog'],
['submitting', null],
['success', 'checkmark']
]);

const trigger =
state == 'submitting' ? (
<Loader active inline size="mini" />
) : (
<Icon
disabled={state !== 'idle'}
title={Translate.string('Actions you can perform on your Zoom meeting')}
color={state === 'success' ? 'green' : undefined}
name={icons.get(state)}
/>
);

return (
<>
<Dropdown trigger={trigger} className="mini button icon">
<Dropdown.Menu>
<Dropdown.Item
title={Translate.string('You will become an alternative host of this Zoom meeting')}
onClick={() => setConfirmOpen(true)}
>
<Icon name="star" />
<Translate>Make me alternative host</Translate>
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
<Confirm
header={Translate.string('Make me an alternative host')}
content={Translate.string('Are you sure you want to be added as an alternative host?')}
open={isConfirmOpen}
onConfirm={makeAlternativeHost}
onCancel={() => setConfirmOpen(false)}
size="tiny"
/>
</>
);
}

interface JoinButtonProps {
classes: string;
href: string;
target: string;
icon: SemanticICONS;
caption: string;
description: string;
altHostUrl: string;
meetingTitle: string | undefined;
meetingDataHtml: string | undefined;
}

/** The join button, which can optionally include an alternative host URL (creates menu) as well as a pop-up */
export default function JoinButton({
classes,
href,
target,
icon,
caption,
description,
altHostUrl = '',
meetingTitle,
meetingDataHtml,
}: JoinButtonProps) {
const [isAltHost, setAltHost] = useState(!altHostUrl);

let buttons = (
<>
<Button size="mini" title={description} className={classes} href={href} target={target}>
<Icon name={icon} />
{caption}
</Button>
{!isAltHost ? (
<OptionsButton
url={altHostUrl}
onMadeAltHost={() => {
setAltHost(true);
}}
/>
) : null}
</>
);

const labeledButton = (
<Button as="div" labelPosition="left" size="mini">
<Label as="a" size="mini" image title="Zoom" pointing="right" styleName="button-label">
<img src={staticURL({filename: 'images/zoom_icon.svg'})} />
</Label>
</Button>
);

buttons = meetingDataHtml ? (
<>
<Popup hoverable trigger={labeledButton}>
<h4>{meetingTitle}</h4>
{/* the HTML data is generated server-side from the Zoom API output and should be in a sanitized state */}
<div dangerouslySetInnerHTML={{__html: meetingDataHtml}} />
</Popup>
{buttons}
</>
) : (
buttons
);

return <ButtonGroup size="mini">{buttons}</ButtonGroup>;
}
46 changes: 46 additions & 0 deletions vc_zoom/indico_vc_zoom/client/ind_vc_zoom_join_button.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// This file is part of the Indico plugins.
// Copyright (C) 2020 - 2024 CERN and ENEA
//
// The Indico plugins are free software; you can redistribute
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.

import React from 'react';
import ReactDOM from 'react-dom';

import JoinButton from './JoinButton';

import './ind_vc_zoom_join_button.scss';

/** Custom element wrapper for a React-managed JoinButton */
customElements.define(
'ind-vc-zoom-join-button',
class extends HTMLElement {
connectedCallback() {
const classes = this.getAttribute('classes');
const href = this.getAttribute('href');
const target = this.getAttribute('target');
const icon = this.getAttribute('icon');
const caption = this.getAttribute('caption');
const description = this.getAttribute('description');
const altHostUrl = this.getAttribute('alt-host-url');
const meetingTitle = this.getAttribute('meeting-title');
const meetingDataHtml = this.getAttribute('meeting-data-html');

ReactDOM.render(
<JoinButton
classes={classes}
href={href}
target={target}
icon={icon}
caption={caption}
description={description}
altHostUrl={altHostUrl}
meetingTitle={meetingTitle}
meetingDataHtml={meetingDataHtml}
/>,
this
);
}
}
);
12 changes: 12 additions & 0 deletions vc_zoom/indico_vc_zoom/client/ind_vc_zoom_join_button.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// This file is part of the Indico plugins.
// Copyright (C) 2020 - 2024 CERN and ENEA
//
// The Indico plugins are free software; you can redistribute
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.

ind-vc-zoom-join-button {
display: flex;
gap: 0.2em;
margin-right: 0.2em;
}
32 changes: 1 addition & 31 deletions vc_zoom/indico_vc_zoom/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,4 @@
// them and/or modify them under the terms of the MIT License;
// see the LICENSE file for more details.

/* global confirmPrompt:false, $T:false */

import {handleAxiosError, indicoAxios} from 'indico/utils/axios';

const $t = $T.domain('vc_zoom');

document.addEventListener('DOMContentLoaded', async () => {
$('.vc-toolbar').dropdown({
positioning: {
level1: {my: 'right top', at: 'right bottom', offset: '0px 0px'},
},
});

document.querySelectorAll('.vc-toolbar .action-make-host').forEach(elem => {
elem.addEventListener('click', () => {
confirmPrompt(
$t.gettext('Are you sure you want to be added as an alternative host?'),
$t.gettext('Make me an alternative host')
).then(async () => {
const killProgress = IndicoUI.Dialogs.Util.progress();
try {
await indicoAxios.post(elem.dataset.href);
window.location.reload();
} catch (error) {
handleAxiosError(error);
killProgress();
}
});
});
});
});
import './ind_vc_zoom_join_button';
9 changes: 3 additions & 6 deletions vc_zoom/indico_vc_zoom/controllers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import hashlib
import hmac

from flask import flash, jsonify, request, session
from flask import jsonify, request, session
from flask_pluginengine import current_plugin
from marshmallow import EXCLUDE
from sqlalchemy.orm.attributes import flag_modified
Expand All @@ -29,8 +29,7 @@ class RHRoomAlternativeHost(RHVCSystemEventBase):
def _process(self):
new_identifier = session.user.identifier
if new_identifier == self.vc_room.data['host'] or new_identifier in self.vc_room.data['alternative_hosts']:
flash(_('You were already an (alternative) host of this meeting'), 'warning')
return jsonify(success=False)
raise UserValueError(_('You were already an (alternative) host of this meeting'))

try:
self.plugin.refresh_room(self.vc_room, self.event)
Expand All @@ -43,9 +42,7 @@ def _process(self):
except VCRoomError:
db.session.rollback()
raise
else:
flash(_("You are now an alternative host of room '{room}'").format(room=self.vc_room.name), 'success')
return jsonify(success=True)
return '', 204


class RHWebhook(RH):
Expand Down
Loading
Loading