-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Add access code for public recordings #6064
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: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -140,11 +140,20 @@ def recordings | |
| # GET /api/v1/rooms/:friendly_id/public_recordings.json | ||
| # Returns all of a specific room's PUBLIC recordings | ||
| def public_recordings | ||
| sort_config = config_sorting(allowed_columns: %w[name length]) | ||
| # Get the recordings access code from the database if it exists | ||
| recordings_access_code = RoomMeetingOption.joins(:meeting_option) | ||
| .where(room_id: @room.id, meeting_options: { name: 'glRecordingsAccessCode' }) | ||
| .first&.value | ||
|
Comment on lines
+144
to
+146
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. It's better to get the value using the RoomSettingsGetter (similar to this) |
||
|
|
||
| # If a recordings access code exists and the provided code doesn't match, require access code | ||
| if recordings_access_code.present? && params[:access_code] != recordings_access_code | ||
| return render_data data: [], meta: { requires_access_code: true }, status: :ok | ||
|
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. I would rather we return something like this instead |
||
| end | ||
|
|
||
| sort_config = config_sorting(allowed_columns: %w[name length]) | ||
| pagy, recordings = pagy(@room.public_recordings.order(sort_config, recorded_at: :desc).public_search(params[:search])) | ||
|
|
||
| render_data data: recordings, meta: pagy_metadata(pagy), serializer: PublicRecordingSerializer, status: :ok | ||
| render_data data: recordings, meta: pagy_metadata(pagy).merge(requires_access_code: false), serializer: PublicRecordingSerializer, status: :ok | ||
|
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. Not quite sure what this is doing, but it shouldn't be doing it |
||
| end | ||
|
|
||
| # GET /api/v1/rooms/:friendly_id/recordings_processing.json | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -227,7 +227,7 @@ export default function JoinCard() { | |
| <h1 className="mt-2"> | ||
| {publicRoom?.data.name} | ||
| </h1> | ||
| { (recordValue !== 'false') && recordings?.data?.length > 0 && ( | ||
| { (recordValue !== 'false') && (recordings?.data?.length > 0 || recordings?.meta?.requires_access_code === true) && ( | ||
|
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. Oh - I would look at how the viewer access code works and implement something similar |
||
| <ButtonLink | ||
| variant="brand-outline" | ||
| className="mt-3 mb-0 cursor-pointer" | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // BigBlueButton open source conferencing system - http://www.bigbluebutton.org/. | ||
| // | ||
| // Copyright (c) 2022 BigBlueButton Inc. and by respective authors (see below). | ||
| // | ||
| // This program is free software; you can redistribute it and/or modify it under the | ||
| // terms of the GNU Lesser General Public License as published by the Free Software | ||
| // Foundation; either version 3.0 of the License, or (at your option) any later | ||
| // version. | ||
| // | ||
| // Greenlight is distributed in the hope that it will be useful, but WITHOUT ANY | ||
| // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | ||
| // PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. | ||
| // | ||
| // You should have received a copy of the GNU Lesser General Public License along | ||
| // with Greenlight; if not, see <http://www.gnu.org/licenses/>. | ||
|
|
||
| import React, { useState } from 'react'; | ||
| import PropTypes from 'prop-types'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import { Form, Button, Stack } from 'react-bootstrap'; | ||
| import ButtonLink from '../../../shared_components/utilities/ButtonLink'; | ||
| import UserBoardIcon from '../../UserBoardIcon'; | ||
|
|
||
| export default function AccessCodeForm({ onAccessCodeSubmit, error, friendlyId }) { | ||
| const { t } = useTranslation(); | ||
| const [accessCode, setAccessCode] = useState(''); | ||
| const [validated] = useState(false); | ||
| const [isSubmitting, setIsSubmitting] = useState(false); | ||
| const [emptyCodeError, setEmptyCodeError] = useState(false); | ||
|
|
||
| const handleSubmit = (e) => { | ||
| e.preventDefault(); | ||
|
|
||
| // Reset error states | ||
| setEmptyCodeError(false); | ||
|
|
||
| // Check for empty access code first | ||
| if (!accessCode?.trim()) { | ||
| setEmptyCodeError(true); | ||
| return; | ||
| } | ||
|
|
||
| setIsSubmitting(true); | ||
|
|
||
| // Call the submit handler and reset submission state when complete | ||
| Promise.resolve(onAccessCodeSubmit(accessCode)) | ||
| .finally(() => { | ||
| setIsSubmitting(false); | ||
| }); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className="text-center p-4"> | ||
| <h2>{t('recording.access_code_required')}</h2> | ||
| <p className="mb-4">{t('recording.enter_access_code_description')}</p> | ||
|
|
||
| <Form noValidate validated={validated} onSubmit={handleSubmit}> | ||
| <Form.Group className="mb-3 d-flex justify-content-center"> | ||
| <div className="position-relative w-50"> | ||
| <Form.Control | ||
| type="text" | ||
| placeholder={t('recording.access_code_placeholder')} | ||
| value={accessCode} | ||
| onChange={(e) => { | ||
| setAccessCode(e.target.value); | ||
| if (e.target.value.trim()) { | ||
| setEmptyCodeError(false); | ||
| } | ||
| }} | ||
| required | ||
| autoFocus | ||
| isInvalid={error || emptyCodeError} | ||
| className={`${(error || emptyCodeError) ? 'border-danger' : ''} text-center`} | ||
| /> | ||
| <Form.Control.Feedback type="invalid" className="text-center"> | ||
| {emptyCodeError && t('room.settings.access_code_required')} | ||
| {error && !emptyCodeError && t('recording.invalid_access_code')} | ||
| </Form.Control.Feedback> | ||
| </div> | ||
| </Form.Group> | ||
|
|
||
| <Stack direction="horizontal" gap={2} className="justify-content-center"> | ||
| <ButtonLink | ||
| variant="brand-outline" | ||
| className="my-0 py-2" | ||
| to={`/rooms/${friendlyId}/join`} | ||
| > | ||
| <span><UserBoardIcon className="hi-s text-brand cursor-pointer" /> {t('join_session')} </span> | ||
| </ButtonLink> | ||
| <Button | ||
| variant="brand" | ||
| type="submit" | ||
| disabled={isSubmitting} | ||
| > | ||
| {isSubmitting ? t('common.submitting') ?? 'Submitting...' : t('recording.submit_access_code')} | ||
| </Button> | ||
| </Stack> | ||
| </Form> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| AccessCodeForm.propTypes = { | ||
| onAccessCodeSubmit: PropTypes.func.isRequired, | ||
| error: PropTypes.bool, | ||
| friendlyId: PropTypes.string.isRequired, | ||
| }; | ||
|
|
||
| AccessCodeForm.defaultProps = { | ||
| error: false, | ||
| }; |
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.
Its better to do this in a data migration