Skip to content

Commit

Permalink
Merge branch 'master' into snyk-fix-55f8e7e4899c93f448e4a3d415ad89ab
Browse files Browse the repository at this point in the history
  • Loading branch information
farhatahmad authored Jul 29, 2024
2 parents 0982e20 + 5b3d161 commit dbf9d3a
Show file tree
Hide file tree
Showing 24 changed files with 563 additions and 28 deletions.
16 changes: 13 additions & 3 deletions app/assets/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,10 @@
"wrong_access_code": "Wrong Access Code",
"generate_viewers_access_code": "Generate access code for viewers",
"generate_mods_access_code": "Generate access code for moderators",
"server_tag": "Select a server type for this room",
"default_tag_name": "Default",
"server_tag_desired": "Desired",
"server_tag_required": "Required",
"are_you_sure_delete_room": "Are you sure you want to delete this room?"
}
},
Expand Down Expand Up @@ -274,8 +278,8 @@
"administration": {
"administration": "Administration",
"terms": "Terms & Conditions",
"privacy": "Privacy Policy",
"privacy_policy": "Privacy Policy",
"privacy": "Privacy Notice",
"privacy_policy": "Privacy Notice",
"change_term_links": "Change the terms links that appears at the bottom of the page",
"change_privacy_link": "Change the privacy link that appears at the bottom of the page",
"helpcenter": "Help Center",
Expand Down Expand Up @@ -413,7 +417,7 @@
"brand_color_updated": "The brand color has been updated.",
"brand_image_updated": "The brand image has been updated.",
"brand_image_deleted": "The brand image has been deleted.",
"privacy_policy_updated": "The privacy policy has been updated.",
"privacy_policy_updated": "The privacy notice has been updated.",
"helpcenter_updated": "The help center link has been updated.",
"terms_of_service_updated": "The terms of service have been updated.",
"maintenance_updated": "The maintenance banner has been updated."
Expand All @@ -437,6 +441,7 @@
},
"error": {
"problem_completing_action": "The action can't be completed. \n Please try again.",
"server_type_unavailable": "The required server type is unavailable. Please select a different type in the room settings.",
"file_type_not_supported": "The file type is not supported.",
"file_size_too_large": "The file size is too large.",
"file_upload_error": "The file can't be uploaded.",
Expand Down Expand Up @@ -533,6 +538,11 @@
},
"url": {
"invalid": "Invalid URL"
},
"text_form": {
"value": {
"required": "Please enter some message"
}
}
},
"room": {
Expand Down
6 changes: 4 additions & 2 deletions app/controllers/api/v1/locales_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ def index
# Returns the requested language's locale strings (returns 406 if locale doesn't exist)
def show
language = params[:name].tr('-', '_')
language_file = Dir.entries('app/assets/locales').select { |f| f.starts_with?(language) }
final_language = language_file.min&.gsub('.json', '')

# Serve locales files directly in development (not through asset pipeline)
return render file: Rails.root.join('app', 'assets', 'locales', "#{language}.json") if Rails.env.development?
return render file: Rails.root.join('app', 'assets', 'locales', "#{final_language}.json") if Rails.env.development?

redirect_to ActionController::Base.helpers.asset_path("#{language}.json")
redirect_to ActionController::Base.helpers.asset_path("#{final_language}.json")
rescue StandardError
head :not_acceptable
end
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/api/v1/meetings_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def start
begin
MeetingStarter.new(room: @room, base_url: request.base_url, current_user:, provider: current_provider).call
rescue BigBlueButton::BigBlueButtonException => e
return render_error status: :bad_request unless e.key == 'idNotUnique'
return render_error status: :bad_request, errors: e.key unless e.key == 'idNotUnique'
end

render_data data: BigBlueButtonApi.new(provider: current_provider).join_meeting(
Expand Down
37 changes: 37 additions & 0 deletions app/controllers/api/v1/server_tags_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 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/>.

# frozen_string_literal: true

module Api
module V1
class ServerTagsController < ApiController
# GET /api/v1/server_tags/:friendly_id
# Returns a list of all allowed tags&names for the room's owner
def show
tag_names = Rails.configuration.server_tag_names
tag_roles = Rails.configuration.server_tag_roles
return render_data data: {}, status: :ok if tag_names.blank?

room = Room.find_by(friendly_id: params[:friendly_id])
return render_data data: {}, status: :ok if room.nil?

allowed_tag_names = tag_names.reject { |tag, _| tag_roles.key?(tag) && tag_roles[tag].exclude?(room.user.role_id) }
render_data data: allowed_tag_names, status: :ok
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
// 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, { useEffect } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
Expand All @@ -25,39 +25,80 @@ import FormControl from '../../../shared_components/forms/FormControl';
import useTextForm from '../../../../hooks/forms/admin/site_settings/useTextForm';

export default function TextForm({ id, value, mutation: useUpdateSiteSettingsAPI }) {
const updateSiteSettingsAPI = useUpdateSiteSettingsAPI();
const updateSiteSettingsAPISetText = useUpdateSiteSettingsAPI();
const updateSiteSettingsAPIClearText = useUpdateSiteSettingsAPI();

const { t } = useTranslation();
const maintenanceBannerId = localStorage.getItem('maintenanceBannerId');

const { methods, fields } = useTextForm({ defaultValues: { value } });

const formText = useRef('');

useEffect(() => {
if (!methods) { return; }
methods.reset({ value });
if (methods) {
methods.reset({ value });
formText.current = value;
}
}, [methods, value]);

const dismissMaintenanceBannerToast = () => {
const maintenanceBannerId = localStorage.getItem('maintenanceBannerId');
if (maintenanceBannerId) {
toast.dismiss(maintenanceBannerId);
localStorage.removeItem('maintenanceBannerId');
}
};

// Function to clear the form
const clearForm = () => {
methods.reset({ value: '' });
toast.dismiss(maintenanceBannerId);
updateSiteSettingsAPI.mutate('');
dismissMaintenanceBannerToast();
if (formText.current) {
formText.current = '';
updateSiteSettingsAPIClearText.mutate('');
}
};

const handleSubmit = useCallback((formData) => {
if (formText.current !== formData[`${fields.value.hookForm.id}`]) {
dismissMaintenanceBannerToast();
formText.current = formData[`${fields.value.hookForm.id}`];
return updateSiteSettingsAPISetText.mutate(formData);
}
return null;
}, [updateSiteSettingsAPISetText.mutate]);

return (
<Form id={id} methods={methods} onSubmit={updateSiteSettingsAPI.mutate}>
<Form id={id} methods={methods} onSubmit={handleSubmit}>
<FormControl
field={fields.value}
aria-describedby={`${id}-submit-btn`}
type="text"
as="textarea"
rows={3}
noLabel
/>
<Button id={`${id}-clear-btn`} className="mb-2 float-end" variant="brand" onClick={clearForm} disabled={updateSiteSettingsAPI.isLoading}>
{updateSiteSettingsAPI.isLoading && <Spinner className="me-2" />}
{ t('admin.site_settings.administration.clear_banner') }
<Button
id={`${id}-clear-btn`}
className="mb-2 float-end"
variant="danger"
onClick={clearForm}
disabled={updateSiteSettingsAPIClearText.isLoading}
>
{updateSiteSettingsAPIClearText.isLoading && (
<Spinner className="me-2" />
)}
{t('admin.site_settings.administration.clear_banner')}
</Button>
<Button id={`${id}-submit-btn`} className="mb-2 float-end me-2" variant="brand" type="submit" disabled={updateSiteSettingsAPI.isLoading}>
{updateSiteSettingsAPI.isLoading && <Spinner className="me-2" />}
{ t('admin.site_settings.administration.set_text') }
<Button
id={`${id}-submit-btn`}
className="mb-2 float-end me-2"
variant="brand"
type="submit"
disabled={updateSiteSettingsAPISetText.isLoading}
>
{updateSiteSettingsAPISetText.isLoading && <Spinner className="me-2" />}
{t('admin.site_settings.administration.set_text')}
</Button>
</Form>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { useAuth } from '../../../../contexts/auth/AuthProvider';
import UpdateRoomNameForm from './forms/UpdateRoomNameForm';
import useRoom from '../../../../hooks/queries/rooms/useRoom';
import UnshareRoom from './UnshareRoom';
import useServerTags from '../../../../hooks/queries/rooms/useServerTags';
import ServerTagRow from './ServerTagRow';

export default function RoomSettings() {
const { t } = useTranslation();
Expand All @@ -41,6 +43,7 @@ export default function RoomSettings() {
const roomSetting = useRoomSettings(friendlyId);
const { data: roomConfigs } = useRoomConfigs();
const { data: room } = useRoom(friendlyId);
const { data: serverTags } = useServerTags(friendlyId);

const updateMutationWrapper = () => useUpdateRoomSetting(friendlyId);
const deleteMutationWrapper = (args) => useDeleteRoom({ friendlyId, ...args });
Expand All @@ -66,6 +69,15 @@ export default function RoomSettings() {
config={roomConfigs?.glModeratorAccessCode}
description={t('room.settings.generate_mods_access_code')}
/>
{serverTags && Object.keys(serverTags).length !== 0 && (
<ServerTagRow
updateMutation={updateMutationWrapper}
currentTag={roomSetting?.data?.serverTag}
tagRequired={roomSetting?.data?.serverTagRequired === 'true'}
serverTags={serverTags}
description={t('room.settings.server_tag')}
/>
)}
</Col>
<Col className="ps-4">
<Row> <h6 className="text-brand">{ t('room.settings.user_settings') }</h6> </Row>
Expand Down
118 changes: 118 additions & 0 deletions app/javascript/components/rooms/room/room_settings/ServerTagRow.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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 from 'react';
import { useTranslation } from 'react-i18next';
import PropTypes from 'prop-types';
import {
Row, Col, Dropdown, ButtonGroup, ToggleButton,
} from 'react-bootstrap';
import SimpleSelect from '../../../shared_components/utilities/SimpleSelect';

export default function ServerTagRow({
updateMutation: useUpdateAPI, currentTag, tagRequired, serverTags, description,
}) {
const updateAPI = useUpdateAPI();
const { t } = useTranslation();

function getDefaultTagName() {
return t('room.settings.default_tag_name');
}

function getTagName(tag) {
if (tag in serverTags) {
return serverTags[tag];
}
return getDefaultTagName();
}

const dropdownTags = Object.entries(serverTags).map(([tagString, tagName]) => (
(
<Dropdown.Item
key={tagString}
value={tagName}
onClick={() => updateAPI.mutate({ settingName: 'serverTag', settingValue: tagString })}
>
{tagName}
</Dropdown.Item>
)
));

return (
<Row>
<h6 className="text-brand">{description}</h6>
<Col>
<SimpleSelect defaultValue={getTagName(currentTag)} dropUp>
{[
<Dropdown.Item
key=""
value={getDefaultTagName()}
disabled={updateAPI.isLoading}
onClick={() => updateAPI.mutate({ settingName: 'serverTag', settingValue: '' })}
>
{getDefaultTagName()}
</Dropdown.Item>,
].concat(dropdownTags)}
</SimpleSelect>
</Col>
<Col>
<ButtonGroup>
<ToggleButton
key="desired"
id="desired"
type="radio"
variant="outline-success"
name="radio"
checked={tagRequired === false}
disabled={updateAPI.isLoading}
onChange={() => {
updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: false });
}}
>
{t('room.settings.server_tag_desired')}
</ToggleButton>
<ToggleButton
key="required"
id="required"
type="radio"
variant="outline-danger"
name="radio"
checked={tagRequired === true}
disabled={updateAPI.isLoading}
onChange={() => {
updateAPI.mutate({ settingName: 'serverTagRequired', settingValue: true });
}}
>
{t('room.settings.server_tag_required')}
</ToggleButton>
</ButtonGroup>
</Col>
</Row>
);
}

ServerTagRow.defaultProps = {
currentTag: '',
tagRequired: false,
};

ServerTagRow.propTypes = {
updateMutation: PropTypes.func.isRequired,
currentTag: PropTypes.string,
tagRequired: PropTypes.bool,
serverTags: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types
description: PropTypes.string.isRequired,
};
5 changes: 4 additions & 1 deletion app/javascript/components/shared_components/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,20 @@ import { useTranslation } from 'react-i18next';
import { Container } from 'react-bootstrap';
import useEnv from '../../hooks/queries/env/useEnv';
import useSiteSetting from '../../hooks/queries/site_settings/useSiteSetting';
import { useAuth } from '../../contexts/auth/AuthProvider';

export default function Footer() {
const { t } = useTranslation();
const { data: env } = useEnv();
const { data: links } = useSiteSetting(['Terms', 'PrivacyPolicy']);
const currentUser = useAuth();
const isAdmin = currentUser && currentUser.role && currentUser?.role.name === 'Administrator';

return (
<footer id="footer" className="footer background-whitesmoke text-center">
<Container id="footer-container" className="py-3">
<a href="https://docs.bigbluebutton.org/greenlight/v3/install" target="_blank" rel="noreferrer">Greenlight</a>
<span className="text-muted"> {env?.VERSION_TAG} </span>
{ isAdmin && <span className="text-muted"> {env?.VERSION_TAG} </span> }
{ links?.Terms
&& (
<a className="ps-3" href={links?.Terms} target="_blank" rel="noreferrer">
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/helpers/FileValidationHelper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ export const handleError = (error, t, toast) => {
toast.error(t('toast.error.file_type_not_supported'));
break;
default:
toast.error(t('toast.error.file_upload_error'));
toast.error(t('toast.error.problem_completing_action'));
}
};
Loading

0 comments on commit dbf9d3a

Please sign in to comment.