diff --git a/bigbluebutton-html5/.gitignore b/bigbluebutton-html5/.gitignore index 3a833eaf4f800bfece09c1cd77ed8cd112714fc2..b8bd4a6520ec7750e5c0d810ffc70d79e9e3e98a 100755 --- a/bigbluebutton-html5/.gitignore +++ b/bigbluebutton-html5/.gitignore @@ -3,5 +3,7 @@ npm-debug.log node_modules/ .meteor/dev_bundle tests/webdriverio/.testing-env +public/locales/de_DE.json +public/locales/ja_JP.json diff --git a/bigbluebutton-html5/imports/startup/client/intl.jsx b/bigbluebutton-html5/imports/startup/client/intl.jsx index 0d44ca2d67522c69ce2c6bcefdf016f449523f8f..c56fac96baa696728478bc9e458028b0b98e5016 100644 --- a/bigbluebutton-html5/imports/startup/client/intl.jsx +++ b/bigbluebutton-html5/imports/startup/client/intl.jsx @@ -63,6 +63,7 @@ class IntlStartup extends Component { fetchLocalizedMessages(locale, init = false) { const url = `./locale?locale=${locale}&init=${init}`; + const localesPath = 'locales'; this.setState({ fetching: true }, () => { fetch(url) @@ -73,11 +74,65 @@ class IntlStartup extends Component { return response.json(); }) - .then(({ messages, normalizedLocale }) => { - const dasherizedLocale = normalizedLocale.replace('_', '-'); - this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => { - IntlStartup.saveLocale(dasherizedLocale); - }); + .then(({ normalizedLocale, regionDefaultLocale }) => { + fetch(`${localesPath}/${DEFAULT_LANGUAGE}.json`) + .then((response) => { + if (!response.ok) { + return Promise.reject(); + } + return response.json(); + }) + .then((messages) => { + if (regionDefaultLocale !== '') { + fetch(`${localesPath}/${regionDefaultLocale}.json`) + .then((response) => { + if (!response.ok) { + return Promise.resolve(); + } + return response.json(); + }) + .then((regionDefaultMessages) => { + messages = Object.assign(messages, regionDefaultMessages); + return messages; + }); + } + + if (normalizedLocale !== DEFAULT_LANGUAGE && normalizedLocale !== regionDefaultLocale) { + fetch(`${localesPath}/${normalizedLocale}.json`) + .then((response) => { + if (!response.ok) { + return Promise.reject(); + } + return response.json(); + }) + .then((localeMessages) => { + messages = Object.assign(messages, localeMessages); + return messages; + }) + .catch(() => { + normalizedLocale = (regionDefaultLocale) || DEFAULT_LANGUAGE; + const dasherizedLocale = normalizedLocale.replace('_', '-'); + this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => { + IntlStartup.saveLocale(normalizedLocale); + }); + }); + } + + return messages; + }) + .then((messages) => { + const dasherizedLocale = normalizedLocale.replace('_', '-'); + this.setState({ messages, fetching: false, normalizedLocale: dasherizedLocale }, () => { + IntlStartup.saveLocale(dasherizedLocale); + }); + }) + .catch(() => { + normalizedLocale = DEFAULT_LANGUAGE; + const dasherizedLocale = normalizedLocale.replace('_', '-'); + this.setState({ fetching: false, normalizedLocale: dasherizedLocale }, () => { + IntlStartup.saveLocale(normalizedLocale); + }); + }); }) .catch(() => { this.setState({ fetching: false, normalizedLocale: null }, () => { diff --git a/bigbluebutton-html5/imports/startup/server/index.js b/bigbluebutton-html5/imports/startup/server/index.js index 7ad0f829dd88987b6f9723b24333698fd15aefb7..5f6508e6a32a1c6ac6d7cf89defff9dae9f2a45d 100755 --- a/bigbluebutton-html5/imports/startup/server/index.js +++ b/bigbluebutton-html5/imports/startup/server/index.js @@ -12,7 +12,16 @@ import Redis from './redis'; import setMinBrowserVersions from './minBrowserVersion'; let guestWaitHtml = ''; -const AVAILABLE_LOCALES = fs.readdirSync('assets/app/locales'); + +const env = Meteor.isDevelopment ? 'development' : 'production'; + +const meteorRoot = fs.realpathSync(`${process.cwd()}/../`); + +const applicationRoot = (env === 'development') + ? fs.realpathSync(`${meteorRoot}'/../../../../public/locales/`) + : fs.realpathSync(`${meteorRoot}/../programs/web.browser/app/locales/`); + +const AVAILABLE_LOCALES = fs.readdirSync(`${applicationRoot}`); const FALLBACK_LOCALES = JSON.parse(Assets.getText('config/fallbackLocales.json')); process.on('uncaughtException', (err) => { @@ -27,7 +36,6 @@ process.on('uncaughtException', (err) => { Meteor.startup(() => { const APP_CONFIG = Meteor.settings.public.app; - const env = Meteor.isDevelopment ? 'development' : 'production'; const CDN_URL = APP_CONFIG.cdn; const instanceId = parseInt(process.env.INSTANCE_ID, 10) || 1; @@ -106,40 +114,39 @@ Meteor.startup(() => { session.bbbFixApplied = true; } }, 5000); + } + if (CDN_URL.trim()) { + // Add CDN + BrowserPolicy.content.disallowEval(); + BrowserPolicy.content.allowInlineScripts(); + BrowserPolicy.content.allowInlineStyles(); + BrowserPolicy.content.allowImageDataUrl(CDN_URL); + BrowserPolicy.content.allowFontDataUrl(CDN_URL); + BrowserPolicy.content.allowOriginForAll(CDN_URL); + WebAppInternals.setBundledJsCssPrefix(CDN_URL + APP_CONFIG.basename + Meteor.settings.public.app.instanceId); + + const fontRegExp = /\.(eot|ttf|otf|woff|woff2)$/; + + WebApp.rawConnectHandlers.use('/', (req, res, next) => { + if (fontRegExp.test(req._parsedUrl.pathname)) { + res.setHeader('Access-Control-Allow-Origin', '*'); + res.setHeader('Vary', 'Origin'); + res.setHeader('Pragma', 'public'); + res.setHeader('Cache-Control', '"public"'); + } + return next(); + }); + } - if (CDN_URL.trim()) { - // Add CDN - BrowserPolicy.content.disallowEval(); - BrowserPolicy.content.allowInlineScripts(); - BrowserPolicy.content.allowInlineStyles(); - BrowserPolicy.content.allowImageDataUrl(CDN_URL); - BrowserPolicy.content.allowFontDataUrl(CDN_URL); - BrowserPolicy.content.allowOriginForAll(CDN_URL); - WebAppInternals.setBundledJsCssPrefix(CDN_URL + APP_CONFIG.basename + Meteor.settings.public.app.instanceId); - - const fontRegExp = /\.(eot|ttf|otf|woff|woff2)$/; - - WebApp.rawConnectHandlers.use('/', (req, res, next) => { - if (fontRegExp.test(req._parsedUrl.pathname)) { - res.setHeader('Access-Control-Allow-Origin', '*'); - res.setHeader('Vary', 'Origin'); - res.setHeader('Pragma', 'public'); - res.setHeader('Cache-Control', '"public"'); - } - return next(); - }); - } - - setMinBrowserVersions(); + setMinBrowserVersions(); - Logger.warn(`SERVER STARTED. - ENV=${env} - nodejs version=${process.version} - BBB_HTML5_ROLE=${process.env.BBB_HTML5_ROLE} - INSTANCE_ID=${instanceId} - PORT=${process.env.PORT} - CDN=${CDN_URL}\n`, APP_CONFIG); - } + Logger.warn(`SERVER STARTED. + ENV=${env} + nodejs version=${process.version} + BBB_HTML5_ROLE=${process.env.BBB_HTML5_ROLE} + INSTANCE_ID=${instanceId} + PORT=${process.env.PORT} + CDN=${CDN_URL}\n`, APP_CONFIG); }); @@ -192,7 +199,7 @@ WebApp.connectHandlers.use('/locale', (req, res) => { const browserLocale = override && req.query.init === 'true' ? override.split(/[-_]/g) : req.query.locale.split(/[-_]/g); - const localeList = [fallback]; + let localeFile = fallback; const usableLocales = AVAILABLE_LOCALES .map(file => file.replace('.json', '')) @@ -200,35 +207,29 @@ WebApp.connectHandlers.use('/locale', (req, res) => { ? [...locales, locale] : locales), []); - const regionDefault = usableLocales.find(locale => browserLocale[0] === locale); - - if (regionDefault) localeList.push(regionDefault); - if (!regionDefault && usableLocales.length) localeList.push(usableLocales[0]); - let normalizedLocale; - let messages = {}; if (browserLocale.length > 1) { normalizedLocale = `${browserLocale[0]}_${browserLocale[1].toUpperCase()}`; - localeList.push(normalizedLocale); + + const normDefault = usableLocales.find(locale => normalizedLocale === locale); + if (normDefault) localeFile = normDefault; } - localeList.forEach((locale) => { - try { - const data = Assets.getText(`locales/${locale}.json`); - messages = Object.assign(messages, JSON.parse(data)); - normalizedLocale = locale; - } catch (e) { - Logger.info(`'Could not process locale ${locale}:${e}`); - // Getting here means the locale is not available in the current locale files. - } - }); + const regionDefault = usableLocales.find(locale => browserLocale[0] === locale); + + if (localeFile === fallback && regionDefault !== localeFile) { + localeFile = regionDefault; + } res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify({ normalizedLocale, messages })); + res.end(JSON.stringify({ + normalizedLocale: localeFile, + regionDefaultLocale: (regionDefault && regionDefault !== localeFile) ? regionDefault : '', + })); }); -WebApp.connectHandlers.use('/locales', (req, res) => { +WebApp.connectHandlers.use('/locale-list', (req, res) => { if (!avaibleLocalesNamesJSON) { avaibleLocalesNamesJSON = JSON.stringify(generateLocaleOptions()); } diff --git a/bigbluebutton-html5/imports/ui/components/legacy/component.jsx b/bigbluebutton-html5/imports/ui/components/legacy/component.jsx index f6c049f3452dc5cc425822c770f4ba119e834c2d..b859270bd6d4c88bf16b98e5fec0126d5cf2b665 100755 --- a/bigbluebutton-html5/imports/ui/components/legacy/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/legacy/component.jsx @@ -69,16 +69,17 @@ const FETCHING = 'fetching'; const FALLBACK = 'fallback'; const READY = 'ready'; const supportedBrowsers = ['chrome', 'firefox', 'safari', 'opera', 'edge', 'yandex']; +const DEFAULT_LANGUAGE = Meteor.settings.public.app.defaultSettings.application.fallbackLocale; export default class Legacy extends Component { constructor(props) { super(props); const locale = navigator.languages ? navigator.languages[0] : false - || navigator.language - || Meteor.settings.public.app.defaultSettings.application.fallbackLocale; + || navigator.language; const url = `./locale?locale=${locale}`; + const localesPath = 'locales'; const that = this; this.state = { viewState: FETCHING }; @@ -90,9 +91,56 @@ export default class Legacy extends Component { return response.json(); }) - .then(({ messages, normalizedLocale }) => { - const dasherizedLocale = normalizedLocale.replace('_', '-'); - that.setState({ messages, normalizedLocale: dasherizedLocale, viewState: READY }); + .then(({ normalizedLocale, regionDefaultLocale }) => { + fetch(`${localesPath}/${DEFAULT_LANGUAGE}.json`) + .then((response) => { + if (!response.ok) { + return Promise.reject(); + } + return response.json(); + }) + .then((messages) => { + if (regionDefaultLocale !== '') { + fetch(`${localesPath}/${regionDefaultLocale}.json`) + .then((response) => { + if (!response.ok) { + return Promise.resolve(); + } + return response.json(); + }) + .then((regionDefaultMessages) => { + messages = Object.assign(messages, regionDefaultMessages); + this.setState({ messages}); + }); + } + + if (normalizedLocale && normalizedLocale !== DEFAULT_LANGUAGE && normalizedLocale !== regionDefaultLocale) { + fetch(`${localesPath}/${normalizedLocale}.json`) + .then((response) => { + if (!response.ok) { + return Promise.reject(); + } + return response.json(); + }) + .then((localeMessages) => { + messages = Object.assign(messages, localeMessages); + this.setState({ messages}); + }) + .catch(() => { + normalizedLocale = (regionDefaultLocale) || DEFAULT_LANGUAGE; + const dasherizedLocale = normalizedLocale.replace('_', '-'); + this.setState({ messages, normalizedLocale: dasherizedLocale, viewState: READY }); + }); + } + return messages; + }) + .then((messages) => { + const dasherizedLocale = normalizedLocale.replace('_', '-'); + this.setState({ messages, normalizedLocale: dasherizedLocale, viewState: READY }); + }) + .catch(() => { + that.setState({ viewState: FALLBACK }); + }); }) .catch(() => { that.setState({ viewState: FALLBACK }); diff --git a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx index a47575fcd678a49504151ef6deb05c5d0257c27b..4c9cf5a67b3915746a348d3e62737ccb0b79f445 100644 --- a/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/media/webcam-draggable-overlay/component.jsx @@ -489,6 +489,7 @@ class WebcamDraggable extends PureComponent { style={{ marginLeft: 0, marginRight: 0, + zIndex: 2, display: hideWebcams ? 'none' : undefined, }} > diff --git a/bigbluebutton-html5/imports/ui/components/poll/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/component.jsx index 479238daf3a535469deafb983858a2435b24680a..5ff55f15e9d32531557f0cfc17b9684f67823e0d 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/poll/component.jsx @@ -9,6 +9,7 @@ import cx from 'classnames'; import Button from '/imports/ui/components/button/component'; import LiveResult from './live-result/component'; import { styles } from './styles.scss'; +import DragAndDrop from './dragAndDrop/component'; const intlMessages = defineMessages({ pollPaneTitle: { @@ -31,6 +32,10 @@ const intlMessages = defineMessages({ id: 'app.poll.activePollInstruction', description: 'instructions displayed when a poll is active', }, + dragDropPollInstruction: { + id: 'app.poll.dragDropPollInstruction', + description: 'instructions for upload poll options via drag and drop', + }, ariaInputCount: { id: 'app.poll.ariaInputCount', description: 'aria label for custom poll input field', @@ -148,6 +153,7 @@ const intlMessages = defineMessages({ const CHAT_ENABLED = Meteor.settings.public.chat.enabled; const MAX_CUSTOM_FIELDS = Meteor.settings.public.poll.max_custom; const MAX_INPUT_CHARS = 45; +const FILE_DRAG_AND_DROP_ENABLED = Meteor.settings.public.poll.allowDragAndDropFile; const validateInput = (i) => { let _input = i; @@ -206,6 +212,21 @@ class Poll extends Component { }); } + handleInputChange(index, event) { + this.handleInputTextChange(index, event.target.value); + } + + handleInputTextChange(index, text) { + const { optList } = this.state; + // This regex will replace any instance of 2 or more consecutive white spaces + // with a single white space character. + const option = text.replace(/\s{2,}/g, ' ').trim(); + + if (index < optList.length) optList[index].val = option === '' ? '' : option; + + this.setState({ optList }); + } + handleInputChange(e, index) { const { optList, type, error } = this.state; const list = [...optList]; @@ -222,6 +243,24 @@ class Poll extends Component { this.setState({ question: validateInput(e.target.value), error: clearError ? null : error }); } + pushToCustomPollValues(text) { + const lines = text.split('\n'); + for (let i = 0; i < MAX_CUSTOM_FIELDS; i += 1) { + let line = ''; + if (i < lines.length) { + line = lines[i]; + line = line.length > MAX_INPUT_CHARS ? line.substring(0, MAX_INPUT_CHARS) : line; + } + this.handleInputTextChange(i, line); + } + } + + handlePollValuesText(text) { + if (text && text.length > 0) { + this.pushToCustomPollValues(text); + } + } + handleRemoveOption(index) { const { optList } = this.state; const list = [...optList]; @@ -496,6 +535,9 @@ class Poll extends Component { }); }} /> + { + FILE_DRAG_AND_DROP_ENABLED && this.renderDragDrop() + } </div> ) } @@ -537,6 +579,25 @@ class Poll extends Component { return this.renderPollOptions(); } + + renderDragDrop() { + const { intl } = this.props; + return ( + <div> + <div className={styles.instructions}> + {intl.formatMessage(intlMessages.dragDropPollInstruction)} + </div> + <DragAndDrop + {...{ intl, MAX_INPUT_CHARS }} + handlePollValuesText={e => this.handlePollValuesText(e)} + > + <div className={styles.dragAndDropPollContainer} /> + </DragAndDrop> + </div> + ); + } + + render() { const { intl, diff --git a/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/component.jsx b/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/component.jsx new file mode 100755 index 0000000000000000000000000000000000000000..b31318d63f85cf7ac179fb68bf256a5796513ca4 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/component.jsx @@ -0,0 +1,143 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { withModalMounter } from '/imports/ui/components/modal/service'; + +import { defineMessages, injectIntl } from 'react-intl'; +import { styles } from './styles.scss'; +import Button from '/imports/ui/components/button/component'; + + +// src: https://medium.com/@650egor/simple-drag-and-drop-file-upload-in-react-2cb409d88929 + +const intlMessages = defineMessages({ + customPollTextArea: { + id: 'app.poll.customPollTextArea', + description: 'label for button to submit custom poll values', + }, +}); + +class DragAndDrop extends Component { + static handleDrag(e) { + e.preventDefault(); + e.stopPropagation(); + } + + constructor(props) { + super(props); + + this.state = { + drag: false, + pollValueText: '', + }; + + this.dropRef = React.createRef(); + } + + componentDidMount() { + this.dragCounter = 0; + const div = this.dropRef.current; + div.addEventListener('dragenter', e => this.handleDragIn(e)); + div.addEventListener('dragleave', e => this.handleDragOut(e)); + div.addEventListener('dragover', e => DragAndDrop.handleDrag(e)); + div.addEventListener('drop', e => this.handleDrop(e)); + } + + componentWillUnmount() { + const div = this.dropRef.current; + div.removeEventListener('dragenter', e => this.handleDragIn(e)); + div.removeEventListener('dragleave', e => this.handleDragOut(e)); + div.removeEventListener('dragover', e => DragAndDrop.handleDrag(e)); + div.removeEventListener('drop', e => this.handleDrop(e)); + } + + setPollValues() { + const { pollValueText } = this.state; + const { handlePollValuesText } = this.props; + if (pollValueText) { + handlePollValuesText(pollValueText); + } + } + + setPollValuesFromFile(file) { + const reader = new FileReader(); + reader.onload = async (e) => { + const text = e.target.result; + this.setPollValueText(text); + this.setPollValues(); + }; + reader.readAsText(file); + } + + setPollValueText(pollText) { + const { MAX_INPUT_CHARS } = this.props; + const arr = pollText.split('\n'); + const text = arr.map(line => (line.length > MAX_INPUT_CHARS ? line.substring(0, MAX_INPUT_CHARS) : line)).join('\n'); + this.setState({ pollValueText: text }); + } + + + handleTextInput(e) { + this.setPollValueText(e.target.value); + } + + + handleDragIn(e) { + DragAndDrop.handleDrag(e); + this.dragCounter += 1; + if (e.dataTransfer.items && e.dataTransfer.items.length > 0) { + this.setState({ drag: true }); + } + } + + handleDragOut(e) { + DragAndDrop.handleDrag(e); + this.dragCounter -= 1; + if (this.dragCounter > 0) return; + this.setState({ drag: false }); + } + + handleDrop(e) { + DragAndDrop.handleDrag(e); + this.setState({ drag: false }); + if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { + this.setPollValuesFromFile(e.dataTransfer.files[0]); + this.dragCounter = 0; + } + } + + + render() { + const { intl, children } = this.props; + const { pollValueText, drag } = this.state; + return ( + <div + className={styles.dndContainer} + ref={this.dropRef} + > + <textarea + value={pollValueText} + className={drag ? styles.dndActive : styles.dndInActive} + onChange={e => this.handleTextInput(e)} + /> + <Button + onClick={() => this.setPollValues()} + label={intl.formatMessage(intlMessages.customPollTextArea)} + color="primary" + disabled={pollValueText < 1} + className={styles.btn} + /> + {children} + </div> + + ); + } +} export default withModalMounter(injectIntl(DragAndDrop)); + +DragAndDrop.propTypes = { + intl: PropTypes.shape({ + formatMessage: PropTypes.func.isRequired, + }).isRequired, + MAX_INPUT_CHARS: PropTypes.number.isRequired, + handlePollValuesText: PropTypes.func.isRequired, + children: PropTypes.element.isRequired, +}; diff --git a/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/styles.scss b/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/styles.scss new file mode 100755 index 0000000000000000000000000000000000000000..abd06217ecf4e5d8ce4d9b48333dfc7ab4eb1b12 --- /dev/null +++ b/bigbluebutton-html5/imports/ui/components/poll/dragAndDrop/styles.scss @@ -0,0 +1,26 @@ +@import "/imports/ui/stylesheets/variables/_all"; + +.dndContainer { + height: 200px; +} + +.customPollValuesTextfield { + width: 100%; + height: 100%; + resize: none; + font-size: var(--font-size-small); +} + +.dndActive { + @extend .customPollValuesTextfield; + background: grey; +} + +.dndInActive { + @extend .customPollValuesTextfield; + background: white; +} + +.btn { + width: 100%; +} diff --git a/bigbluebutton-html5/imports/ui/components/poll/styles.scss b/bigbluebutton-html5/imports/ui/components/poll/styles.scss index d2e1953cdb266325c91c8c6d45ced32528841697..775d7d23a23700f4938566666300363e47b9f21a 100644 --- a/bigbluebutton-html5/imports/ui/components/poll/styles.scss +++ b/bigbluebutton-html5/imports/ui/components/poll/styles.scss @@ -356,3 +356,8 @@ color: var(--color-white) !important; } } + +.dragAndDropPollContainer { + width: 200px !important; + height: 200px !important; +} diff --git a/bigbluebutton-html5/imports/ui/components/settings/service.js b/bigbluebutton-html5/imports/ui/components/settings/service.js index 85af45dddd425ef6af2f48f04cd3115870e57262..42698cb30c268933700445e9841c7c8ee30ad0a4 100644 --- a/bigbluebutton-html5/imports/ui/components/settings/service.js +++ b/bigbluebutton-html5/imports/ui/components/settings/service.js @@ -27,7 +27,7 @@ const updateSettings = (obj, msg) => { } }; -const getAvailableLocales = () => fetch('./locales').then(locales => locales.json()); +const getAvailableLocales = () => fetch('./locale-list').then(locales => locales.json()); export { getUserRoles, diff --git a/bigbluebutton-html5/private/config/settings.yml b/bigbluebutton-html5/private/config/settings.yml index 478f587da062616d9e447f9a31ff7edbd11725b0..38ac61e6cfa011baab6b10ff88c3f8878aedf4f3 100755 --- a/bigbluebutton-html5/private/config/settings.yml +++ b/bigbluebutton-html5/private/config/settings.yml @@ -295,6 +295,7 @@ public: poll: enabled: true max_custom: 5 + allowDragAndDropFile: false captions: enabled: true enableDictation: false diff --git a/bigbluebutton-html5/private/locales/ar.json b/bigbluebutton-html5/public/locales/ar.json similarity index 100% rename from bigbluebutton-html5/private/locales/ar.json rename to bigbluebutton-html5/public/locales/ar.json diff --git a/bigbluebutton-html5/private/locales/az.json b/bigbluebutton-html5/public/locales/az.json similarity index 100% rename from bigbluebutton-html5/private/locales/az.json rename to bigbluebutton-html5/public/locales/az.json diff --git a/bigbluebutton-html5/private/locales/bg_BG.json b/bigbluebutton-html5/public/locales/bg_BG.json similarity index 100% rename from bigbluebutton-html5/private/locales/bg_BG.json rename to bigbluebutton-html5/public/locales/bg_BG.json diff --git a/bigbluebutton-html5/private/locales/ca.json b/bigbluebutton-html5/public/locales/ca.json similarity index 100% rename from bigbluebutton-html5/private/locales/ca.json rename to bigbluebutton-html5/public/locales/ca.json diff --git a/bigbluebutton-html5/private/locales/cs_CZ.json b/bigbluebutton-html5/public/locales/cs_CZ.json similarity index 100% rename from bigbluebutton-html5/private/locales/cs_CZ.json rename to bigbluebutton-html5/public/locales/cs_CZ.json diff --git a/bigbluebutton-html5/private/locales/da.json b/bigbluebutton-html5/public/locales/da.json similarity index 100% rename from bigbluebutton-html5/private/locales/da.json rename to bigbluebutton-html5/public/locales/da.json diff --git a/bigbluebutton-html5/private/locales/de.json b/bigbluebutton-html5/public/locales/de.json similarity index 99% rename from bigbluebutton-html5/private/locales/de.json rename to bigbluebutton-html5/public/locales/de.json index 53555c2f9daf9d8546add45088bbbbc0e4cf9455..a2776b6914f13f8c6182961753fa6f71c630d896 100644 --- a/bigbluebutton-html5/private/locales/de.json +++ b/bigbluebutton-html5/public/locales/de.json @@ -219,6 +219,10 @@ "app.poll.hidePollDesc": "Versteckt das Umfragemenü", "app.poll.quickPollInstruction": "Wählen Sie eine der unten stehenden Optionen, um die Umfrage zu starten.", "app.poll.activePollInstruction": "Lassen Sie dieses Fenster offen, um auf die Antworten der Teilnehmer zu warten. Sobald Sie auf 'Umfrageergebnisse veröffentlichen' klicken, werden die Ergebnisse angezeigt und die Umfrage beendet.", + "app.poll.customPollLabel": "Benutzerdefinierte Umfrage", + "app.poll.startCustomLabel": "Benutzerdefinierte Umfrage starten", + "app.poll.dragDropPollInstruction": "Ziehen Sie per drag and Drop eine Textdatei mit den Umfrageoptionen auf das markierte Feld", + "app.poll.customPollTextArea": "Umfrageoptionen ausfüllen", "app.poll.publishLabel": "Umfrageergebnisse veröffentlichen", "app.poll.backLabel": "Umfrage starten", "app.poll.closeLabel": "Schließen", diff --git a/bigbluebutton-html5/private/locales/el_GR.json b/bigbluebutton-html5/public/locales/el_GR.json similarity index 100% rename from bigbluebutton-html5/private/locales/el_GR.json rename to bigbluebutton-html5/public/locales/el_GR.json diff --git a/bigbluebutton-html5/private/locales/en.json b/bigbluebutton-html5/public/locales/en.json similarity index 99% rename from bigbluebutton-html5/private/locales/en.json rename to bigbluebutton-html5/public/locales/en.json index b7d464fd7237de9bdf7fce11fccfa28f6e0f2ed7..36074d87d8ed2a5de5726783b66e62a967b37969 100755 --- a/bigbluebutton-html5/private/locales/en.json +++ b/bigbluebutton-html5/public/locales/en.json @@ -221,6 +221,8 @@ "app.poll.hidePollDesc": "Hides the poll menu pane", "app.poll.quickPollInstruction": "Select an option below to start your poll.", "app.poll.activePollInstruction": "Leave this panel open to see live responses to your poll. When you are ready, select 'Publish polling results' to publish the results and end the poll.", + "app.poll.dragDropPollInstruction": "To fill the poll values, drag a text file with the poll values onto the highlighted field", + "app.poll.customPollTextArea": "Fill poll values", "app.poll.publishLabel": "Publish polling results", "app.poll.backLabel": "Start A Poll", "app.poll.closeLabel": "Close", diff --git a/bigbluebutton-html5/private/locales/eo.json b/bigbluebutton-html5/public/locales/eo.json similarity index 100% rename from bigbluebutton-html5/private/locales/eo.json rename to bigbluebutton-html5/public/locales/eo.json diff --git a/bigbluebutton-html5/private/locales/es.json b/bigbluebutton-html5/public/locales/es.json similarity index 100% rename from bigbluebutton-html5/private/locales/es.json rename to bigbluebutton-html5/public/locales/es.json diff --git a/bigbluebutton-html5/private/locales/es_ES.json b/bigbluebutton-html5/public/locales/es_ES.json similarity index 100% rename from bigbluebutton-html5/private/locales/es_ES.json rename to bigbluebutton-html5/public/locales/es_ES.json diff --git a/bigbluebutton-html5/private/locales/es_MX.json b/bigbluebutton-html5/public/locales/es_MX.json similarity index 100% rename from bigbluebutton-html5/private/locales/es_MX.json rename to bigbluebutton-html5/public/locales/es_MX.json diff --git a/bigbluebutton-html5/private/locales/et.json b/bigbluebutton-html5/public/locales/et.json similarity index 100% rename from bigbluebutton-html5/private/locales/et.json rename to bigbluebutton-html5/public/locales/et.json diff --git a/bigbluebutton-html5/private/locales/eu.json b/bigbluebutton-html5/public/locales/eu.json similarity index 100% rename from bigbluebutton-html5/private/locales/eu.json rename to bigbluebutton-html5/public/locales/eu.json diff --git a/bigbluebutton-html5/private/locales/fa_IR.json b/bigbluebutton-html5/public/locales/fa_IR.json similarity index 100% rename from bigbluebutton-html5/private/locales/fa_IR.json rename to bigbluebutton-html5/public/locales/fa_IR.json diff --git a/bigbluebutton-html5/private/locales/fi.json b/bigbluebutton-html5/public/locales/fi.json similarity index 100% rename from bigbluebutton-html5/private/locales/fi.json rename to bigbluebutton-html5/public/locales/fi.json diff --git a/bigbluebutton-html5/private/locales/fr.json b/bigbluebutton-html5/public/locales/fr.json similarity index 100% rename from bigbluebutton-html5/private/locales/fr.json rename to bigbluebutton-html5/public/locales/fr.json diff --git a/bigbluebutton-html5/private/locales/gl.json b/bigbluebutton-html5/public/locales/gl.json similarity index 100% rename from bigbluebutton-html5/private/locales/gl.json rename to bigbluebutton-html5/public/locales/gl.json diff --git a/bigbluebutton-html5/private/locales/he.json b/bigbluebutton-html5/public/locales/he.json similarity index 100% rename from bigbluebutton-html5/private/locales/he.json rename to bigbluebutton-html5/public/locales/he.json diff --git a/bigbluebutton-html5/private/locales/hi_IN.json b/bigbluebutton-html5/public/locales/hi_IN.json similarity index 100% rename from bigbluebutton-html5/private/locales/hi_IN.json rename to bigbluebutton-html5/public/locales/hi_IN.json diff --git a/bigbluebutton-html5/private/locales/hr.json b/bigbluebutton-html5/public/locales/hr.json similarity index 100% rename from bigbluebutton-html5/private/locales/hr.json rename to bigbluebutton-html5/public/locales/hr.json diff --git a/bigbluebutton-html5/private/locales/hu_HU.json b/bigbluebutton-html5/public/locales/hu_HU.json similarity index 100% rename from bigbluebutton-html5/private/locales/hu_HU.json rename to bigbluebutton-html5/public/locales/hu_HU.json diff --git a/bigbluebutton-html5/private/locales/hy.json b/bigbluebutton-html5/public/locales/hy.json similarity index 100% rename from bigbluebutton-html5/private/locales/hy.json rename to bigbluebutton-html5/public/locales/hy.json diff --git a/bigbluebutton-html5/private/locales/id.json b/bigbluebutton-html5/public/locales/id.json similarity index 100% rename from bigbluebutton-html5/private/locales/id.json rename to bigbluebutton-html5/public/locales/id.json diff --git a/bigbluebutton-html5/private/locales/it_IT.json b/bigbluebutton-html5/public/locales/it_IT.json similarity index 100% rename from bigbluebutton-html5/private/locales/it_IT.json rename to bigbluebutton-html5/public/locales/it_IT.json diff --git a/bigbluebutton-html5/private/locales/ja.json b/bigbluebutton-html5/public/locales/ja.json similarity index 100% rename from bigbluebutton-html5/private/locales/ja.json rename to bigbluebutton-html5/public/locales/ja.json diff --git a/bigbluebutton-html5/private/locales/ka.json b/bigbluebutton-html5/public/locales/ka.json similarity index 100% rename from bigbluebutton-html5/private/locales/ka.json rename to bigbluebutton-html5/public/locales/ka.json diff --git a/bigbluebutton-html5/private/locales/kk.json b/bigbluebutton-html5/public/locales/kk.json similarity index 100% rename from bigbluebutton-html5/private/locales/kk.json rename to bigbluebutton-html5/public/locales/kk.json diff --git a/bigbluebutton-html5/private/locales/km.json b/bigbluebutton-html5/public/locales/km.json similarity index 100% rename from bigbluebutton-html5/private/locales/km.json rename to bigbluebutton-html5/public/locales/km.json diff --git a/bigbluebutton-html5/private/locales/kn.json b/bigbluebutton-html5/public/locales/kn.json similarity index 100% rename from bigbluebutton-html5/private/locales/kn.json rename to bigbluebutton-html5/public/locales/kn.json diff --git a/bigbluebutton-html5/private/locales/ko_KR.json b/bigbluebutton-html5/public/locales/ko_KR.json similarity index 100% rename from bigbluebutton-html5/private/locales/ko_KR.json rename to bigbluebutton-html5/public/locales/ko_KR.json diff --git a/bigbluebutton-html5/private/locales/lo_LA.json b/bigbluebutton-html5/public/locales/lo_LA.json similarity index 100% rename from bigbluebutton-html5/private/locales/lo_LA.json rename to bigbluebutton-html5/public/locales/lo_LA.json diff --git a/bigbluebutton-html5/private/locales/lt_LT.json b/bigbluebutton-html5/public/locales/lt_LT.json similarity index 100% rename from bigbluebutton-html5/private/locales/lt_LT.json rename to bigbluebutton-html5/public/locales/lt_LT.json diff --git a/bigbluebutton-html5/private/locales/lv.json b/bigbluebutton-html5/public/locales/lv.json similarity index 100% rename from bigbluebutton-html5/private/locales/lv.json rename to bigbluebutton-html5/public/locales/lv.json diff --git a/bigbluebutton-html5/private/locales/mn_MN.json b/bigbluebutton-html5/public/locales/mn_MN.json similarity index 100% rename from bigbluebutton-html5/private/locales/mn_MN.json rename to bigbluebutton-html5/public/locales/mn_MN.json diff --git a/bigbluebutton-html5/private/locales/nb_NO.json b/bigbluebutton-html5/public/locales/nb_NO.json similarity index 100% rename from bigbluebutton-html5/private/locales/nb_NO.json rename to bigbluebutton-html5/public/locales/nb_NO.json diff --git a/bigbluebutton-html5/private/locales/nl.json b/bigbluebutton-html5/public/locales/nl.json similarity index 100% rename from bigbluebutton-html5/private/locales/nl.json rename to bigbluebutton-html5/public/locales/nl.json diff --git a/bigbluebutton-html5/private/locales/oc.json b/bigbluebutton-html5/public/locales/oc.json similarity index 100% rename from bigbluebutton-html5/private/locales/oc.json rename to bigbluebutton-html5/public/locales/oc.json diff --git a/bigbluebutton-html5/private/locales/pl_PL.json b/bigbluebutton-html5/public/locales/pl_PL.json similarity index 100% rename from bigbluebutton-html5/private/locales/pl_PL.json rename to bigbluebutton-html5/public/locales/pl_PL.json diff --git a/bigbluebutton-html5/private/locales/pt.json b/bigbluebutton-html5/public/locales/pt.json similarity index 100% rename from bigbluebutton-html5/private/locales/pt.json rename to bigbluebutton-html5/public/locales/pt.json diff --git a/bigbluebutton-html5/private/locales/pt_BR.json b/bigbluebutton-html5/public/locales/pt_BR.json similarity index 100% rename from bigbluebutton-html5/private/locales/pt_BR.json rename to bigbluebutton-html5/public/locales/pt_BR.json diff --git a/bigbluebutton-html5/private/locales/ro_RO.json b/bigbluebutton-html5/public/locales/ro_RO.json similarity index 100% rename from bigbluebutton-html5/private/locales/ro_RO.json rename to bigbluebutton-html5/public/locales/ro_RO.json diff --git a/bigbluebutton-html5/private/locales/ru.json b/bigbluebutton-html5/public/locales/ru.json similarity index 100% rename from bigbluebutton-html5/private/locales/ru.json rename to bigbluebutton-html5/public/locales/ru.json diff --git a/bigbluebutton-html5/private/locales/ru_RU.json b/bigbluebutton-html5/public/locales/ru_RU.json similarity index 100% rename from bigbluebutton-html5/private/locales/ru_RU.json rename to bigbluebutton-html5/public/locales/ru_RU.json diff --git a/bigbluebutton-html5/private/locales/sk_SK.json b/bigbluebutton-html5/public/locales/sk_SK.json similarity index 100% rename from bigbluebutton-html5/private/locales/sk_SK.json rename to bigbluebutton-html5/public/locales/sk_SK.json diff --git a/bigbluebutton-html5/private/locales/sl.json b/bigbluebutton-html5/public/locales/sl.json similarity index 100% rename from bigbluebutton-html5/private/locales/sl.json rename to bigbluebutton-html5/public/locales/sl.json diff --git a/bigbluebutton-html5/private/locales/sr.json b/bigbluebutton-html5/public/locales/sr.json similarity index 100% rename from bigbluebutton-html5/private/locales/sr.json rename to bigbluebutton-html5/public/locales/sr.json diff --git a/bigbluebutton-html5/private/locales/sv_SE.json b/bigbluebutton-html5/public/locales/sv_SE.json similarity index 100% rename from bigbluebutton-html5/private/locales/sv_SE.json rename to bigbluebutton-html5/public/locales/sv_SE.json diff --git a/bigbluebutton-html5/private/locales/te.json b/bigbluebutton-html5/public/locales/te.json similarity index 100% rename from bigbluebutton-html5/private/locales/te.json rename to bigbluebutton-html5/public/locales/te.json diff --git a/bigbluebutton-html5/private/locales/th.json b/bigbluebutton-html5/public/locales/th.json similarity index 100% rename from bigbluebutton-html5/private/locales/th.json rename to bigbluebutton-html5/public/locales/th.json diff --git a/bigbluebutton-html5/private/locales/th_TH.json b/bigbluebutton-html5/public/locales/th_TH.json similarity index 100% rename from bigbluebutton-html5/private/locales/th_TH.json rename to bigbluebutton-html5/public/locales/th_TH.json diff --git a/bigbluebutton-html5/private/locales/tr.json b/bigbluebutton-html5/public/locales/tr.json similarity index 100% rename from bigbluebutton-html5/private/locales/tr.json rename to bigbluebutton-html5/public/locales/tr.json diff --git a/bigbluebutton-html5/private/locales/tr_TR.json b/bigbluebutton-html5/public/locales/tr_TR.json similarity index 100% rename from bigbluebutton-html5/private/locales/tr_TR.json rename to bigbluebutton-html5/public/locales/tr_TR.json diff --git a/bigbluebutton-html5/private/locales/uk_UA.json b/bigbluebutton-html5/public/locales/uk_UA.json similarity index 100% rename from bigbluebutton-html5/private/locales/uk_UA.json rename to bigbluebutton-html5/public/locales/uk_UA.json diff --git a/bigbluebutton-html5/private/locales/vi.json b/bigbluebutton-html5/public/locales/vi.json similarity index 100% rename from bigbluebutton-html5/private/locales/vi.json rename to bigbluebutton-html5/public/locales/vi.json diff --git a/bigbluebutton-html5/private/locales/vi_VN.json b/bigbluebutton-html5/public/locales/vi_VN.json similarity index 100% rename from bigbluebutton-html5/private/locales/vi_VN.json rename to bigbluebutton-html5/public/locales/vi_VN.json diff --git a/bigbluebutton-html5/private/locales/zh_CN.json b/bigbluebutton-html5/public/locales/zh_CN.json similarity index 100% rename from bigbluebutton-html5/private/locales/zh_CN.json rename to bigbluebutton-html5/public/locales/zh_CN.json diff --git a/bigbluebutton-html5/private/locales/zh_TW.json b/bigbluebutton-html5/public/locales/zh_TW.json similarity index 100% rename from bigbluebutton-html5/private/locales/zh_TW.json rename to bigbluebutton-html5/public/locales/zh_TW.json diff --git a/bigbluebutton-html5/transifex.sh b/bigbluebutton-html5/transifex.sh index de1efe3eaa73f91ca62b80f75b450bf59da70587..b11dc50f7ac3f69058c0106abd1dfa425230b819 100755 --- a/bigbluebutton-html5/transifex.sh +++ b/bigbluebutton-html5/transifex.sh @@ -5,6 +5,14 @@ RED='\033[0;31m' GREEN='\033[1;32m' NC='\033[0m' SOURCE_LANGUAGE="en" +LOCALES_DIRECTORY="./public/locales" +PULL_SOURCE=false + +if [[ ! -e $LOCALES_DIRECTORY ]]; then + echo -e "Directory ${RED}$LOCALES_DIRECTORY${NC} does not exist, creating" + mkdir $LOCALES_DIRECTORY + PULL_SOURCE=true +fi if [ "$#" = 0 ] then @@ -33,19 +41,19 @@ else echo "$AVAILABLE_TRANSLATIONS" | while read l do LOCALE=$( echo "$l" | tr -d '[:space:]' ) - if [ "$LOCALE" == "$SOURCE_LANGUAGE" ]; then - continue # do not pull the source file + if [ "$LOCALE" == "$SOURCE_LANGUAGE" ] && [ "$PULL_SOURCE" == false ]; then + continue # only pull source file if locales folder did not exist fi TRANSLATION=$(curl -L --user "$USER":"$PW" -X GET "https://www.transifex.com/api/2/project/bigbluebutton-v23-html5-client/resource/enjson/translation/$LOCALE/?mode=onlytranslated&file") NO_EMPTY_STRINGS=$(echo "$TRANSLATION" | sed '/: *\"\"/D' | sed '/}$/D') - if [ $(echo "$NO_EMPTY_STRINGS" | wc -l) == 1 ] + if [ $(echo "$NO_EMPTY_STRINGS" | wc -l) -lt 100 ] then - echo -e "${RED}WARN:${NC} translation file $LOCALE.json is empty\n${RED}WARN:${NC} $LOCALE.json not created" + echo -e "${RED}WARN:${NC} translation file $LOCALE.json contains less than 100 lines\n${RED}WARN:${NC} $LOCALE.json not created" continue else NO_TRAILING_COMMA=$(echo "$NO_EMPTY_STRINGS" | sed '$ s/,$//') - echo "$NO_TRAILING_COMMA" > ./private/locales/"$LOCALE".json - echo -e "\n}\n" >> ./private/locales/"$LOCALE".json + echo "$NO_TRAILING_COMMA" > "$LOCALES_DIRECTORY/$LOCALE".json + echo -e "\n}\n" >> "$LOCALES_DIRECTORY/$LOCALE".json echo -e "Added translation file $LOCALE.json : ${GREEN}✓${NC}" fi done @@ -56,13 +64,13 @@ else echo -e "${RED}Err${NC}: Translations not found for locale ->${RED}$ARG${NC}<-" else NO_EMPTY_STRINGS=$(echo "$TRANSLATION" | sed '/: *\"\"/D' | sed '/}$/D') - if [ $(echo "$NO_EMPTY_STRINGS" | wc -l) == 1 ] + if [ $(echo "$NO_EMPTY_STRINGS" | wc -l) -lt 100 ] then - echo -e "${RED}WARN:${NC} translation file $ARG.json is empty\n${RED}WARN:${NC} $ARG.json not created" + echo -e "${RED}WARN:${NC} translation file $ARG.json contains less than 100 lines\n${RED}WARN:${NC} $ARG.json not created" else NO_TRAILING_COMMA=$(echo "$NO_EMPTY_STRINGS" | sed '$ s/,//') - echo "$NO_TRAILING_COMMA" > ./private/locales/"$ARG".json - echo -e "\n}\n" >> ./private/locales/"$ARG".json + echo "$NO_TRAILING_COMMA" > "$LOCALES_DIRECTORY/$ARG".json + echo -e "\n}\n" >> "$LOCALES_DIRECTORY/$ARG".json echo -e "Added translation file $ARG.json :${GREEN} ✓${NC}" fi fi