Merge branch 'v2.2.1-alpha' into master

This commit is contained in:
Jesus Federico 2019-07-23 15:57:08 -04:00 committed by GitHub
commit f7c88cfc6a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
77 changed files with 1740 additions and 921 deletions

View File

@ -1,7 +1,14 @@
FROM ruby:2.5
# Install app dependencies.
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev curl
ADD https://dl.yarnpkg.com/debian/pubkey.gpg /tmp/yarn-pubkey.gpg
RUN apt-key add /tmp/yarn-pubkey.gpg && rm /tmp/yarn-pubkey.gpg && \
echo 'deb http://dl.yarnpkg.com/debian/ stable main' > /etc/apt/sources.list.d/yarn.list && \
curl -sL https://deb.nodesource.com/setup_10.x | bash - && \
apt-get update && apt-get install -y nodejs yarn
# Set an environment variable for the install location.
ENV RAILS_ROOT /usr/src/app
@ -23,9 +30,9 @@ RUN bundle install --without development test --deployment --clean
# Adding project files.
COPY . .
# Precompile assets.
RUN bundle exec rake assets:clean
RUN bundle exec rake assets:precompile
# Precompile assets
RUN SECRET_KEY_BASE="$(bundle exec rake secret)" bundle exec rake assets:clean
RUN SECRET_KEY_BASE="$(bundle exec rake secret)" bundle exec rake assets:precompile
# Expose port 80.
EXPOSE 80

11
Gemfile
View File

@ -8,13 +8,13 @@ git_source(:github) do |repo_name|
end
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.7'
gem 'rails', '~> 5.2.3'
# Use Puma as the app server
gem 'puma', '~> 3.0'
# Use SCSS for stylesheets
gem 'sass-rails', '~> 5.0'
gem 'sassc-rails'
# Use Uglifier as compressor for JavaScript assets
gem 'uglifier', '>= 1.3.0'
@ -35,11 +35,14 @@ gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.5'
# Use Redis adapter to run Action Cable in production
gem 'redis', '~> 3.0'
# gem 'redis', '~> 3.0'
# Use ActiveModel has_secure_password
gem 'bcrypt', '~> 3.1.7'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
# Authentication.
gem 'omniauth'
gem 'omniauth-twitter'
@ -54,7 +57,7 @@ gem 'bigbluebutton-api-ruby'
# Front-end.
gem 'bootstrap', '~> 4.3.1'
gem 'tabler-rubygem'
gem 'tabler-rubygem', git: 'https://github.com/vbalazs/tabler-rubygem.git', branch: 'fix-sass'
gem 'pagy'
# For detecting the users preferred language.

View File

@ -23,58 +23,73 @@ GIT
omniauth (>= 1.3.2)
omniauth-oauth2 (>= 1.5.0)
GIT
remote: https://github.com/vbalazs/tabler-rubygem.git
revision: 1360795a174f6e6317eb6c81461597ddc965c2ce
branch: fix-sass
specs:
tabler-rubygem (0.1.4)
autoprefixer-rails (>= 6.0.3)
bootstrap (~> 4.3.1)
GEM
remote: https://rubygems.org/
specs:
action-cable-testing (0.5.0)
actioncable (>= 5.0)
actioncable (5.0.7.2)
actionpack (= 5.0.7.2)
nio4r (>= 1.2, < 3.0)
websocket-driver (~> 0.6.1)
actionmailer (5.0.7.2)
actionpack (= 5.0.7.2)
actionview (= 5.0.7.2)
activejob (= 5.0.7.2)
actioncable (5.2.3)
actionpack (= 5.2.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailer (5.2.3)
actionpack (= 5.2.3)
actionview (= 5.2.3)
activejob (= 5.2.3)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (5.0.7.2)
actionview (= 5.0.7.2)
activesupport (= 5.0.7.2)
actionpack (5.2.3)
actionview (= 5.2.3)
activesupport (= 5.2.3)
rack (~> 2.0)
rack-test (~> 0.6.3)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.2)
actionview (5.0.7.2)
activesupport (= 5.0.7.2)
actionview (5.2.3)
activesupport (= 5.2.3)
builder (~> 3.1)
erubis (~> 2.7.0)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.0.3)
activejob (5.0.7.2)
activesupport (= 5.0.7.2)
activejob (5.2.3)
activesupport (= 5.2.3)
globalid (>= 0.3.6)
activemodel (5.0.7.2)
activesupport (= 5.0.7.2)
activerecord (5.0.7.2)
activemodel (= 5.0.7.2)
activesupport (= 5.0.7.2)
arel (~> 7.0)
activesupport (5.0.7.2)
activemodel (5.2.3)
activesupport (= 5.2.3)
activerecord (5.2.3)
activemodel (= 5.2.3)
activesupport (= 5.2.3)
arel (>= 9.0)
activestorage (5.2.3)
actionpack (= 5.2.3)
activerecord (= 5.2.3)
marcel (~> 0.3.1)
activesupport (5.2.3)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
addressable (2.6.0)
public_suffix (>= 2.0.2, < 4.0)
arel (7.1.4)
arel (9.0.0)
ast (2.4.0)
autoprefixer-rails (9.5.1.1)
autoprefixer-rails (9.6.1)
execjs
bcrypt (3.1.12)
bcrypt (3.1.13)
bigbluebutton-api-ruby (1.7.0)
xml-simple (~> 1.1)
bindex (0.7.0)
bindex (0.8.1)
bootsnap (1.4.4)
msgpack (~> 1.0)
bootstrap (4.3.1)
autoprefixer-rails (>= 9.1.0)
popper_js (>= 1.14.3, < 2)
@ -100,26 +115,26 @@ GEM
safe_yaml (~> 1.0.0)
crass (1.0.4)
diff-lcs (1.3)
docile (1.3.1)
dotenv (2.7.2)
dotenv-rails (2.7.2)
dotenv (= 2.7.2)
docile (1.3.2)
dotenv (2.7.4)
dotenv-rails (2.7.4)
dotenv (= 2.7.4)
railties (>= 3.2, < 6.1)
erubis (2.7.0)
erubi (1.8.0)
execjs (2.7.0)
factory_bot (5.0.2)
activesupport (>= 4.2.0)
factory_bot_rails (5.0.2)
factory_bot (~> 5.0.2)
railties (>= 4.2.0)
faker (1.9.3)
faker (1.9.6)
i18n (>= 0.7)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
ffi (1.11.1)
globalid (0.4.2)
activesupport (>= 4.2.0)
hashdiff (0.3.9)
hashdiff (0.4.0)
hashie (3.6.0)
health_check (3.0.0)
railties (>= 5.0)
@ -127,15 +142,15 @@ GEM
i18n (1.6.0)
concurrent-ruby (~> 1.0)
i18n-language-mapping (0.1.0)
jaro_winkler (1.5.2)
jaro_winkler (1.5.3)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
jquery-rails (4.3.3)
jquery-rails (4.3.5)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.2.0)
jwt (2.1.0)
jwt (2.2.1)
libv8 (7.3.492.27.1)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
@ -145,17 +160,21 @@ GEM
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
method_source (0.9.2)
mini_mime (1.0.1)
mimemagic (0.3.3)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
mini_racer (0.2.6)
libv8 (>= 6.9.411)
minitest (5.11.3)
msgpack (1.3.0)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.1.1)
net-ldap (0.16.1)
nio4r (2.3.1)
nio4r (2.4.0)
nokogiri (1.10.3)
mini_portile2 (~> 2.4.0)
oauth (0.5.4)
@ -168,7 +187,7 @@ GEM
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
rack (>= 1.6.2, < 3)
omniauth-google-oauth2 (0.6.1)
omniauth-google-oauth2 (0.7.0)
jwt (>= 2.0)
omniauth (>= 1.1.1)
omniauth-oauth2 (>= 1.5)
@ -181,28 +200,29 @@ GEM
omniauth-twitter (1.4.0)
omniauth-oauth (~> 1.1)
rack
pagy (3.2.0)
pagy (3.3.2)
parallel (1.17.0)
parser (2.6.3.0)
ast (~> 2.4.0)
pg (0.21.0)
popper_js (1.14.5)
public_suffix (3.0.3)
public_suffix (3.1.1)
puma (3.12.1)
rack (2.0.7)
rack-test (0.6.3)
rack (>= 1.0)
rails (5.0.7.2)
actioncable (= 5.0.7.2)
actionmailer (= 5.0.7.2)
actionpack (= 5.0.7.2)
actionview (= 5.0.7.2)
activejob (= 5.0.7.2)
activemodel (= 5.0.7.2)
activerecord (= 5.0.7.2)
activesupport (= 5.0.7.2)
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (5.2.3)
actioncable (= 5.2.3)
actionmailer (= 5.2.3)
actionpack (= 5.2.3)
actionview (= 5.2.3)
activejob (= 5.2.3)
activemodel (= 5.2.3)
activerecord (= 5.2.3)
activestorage (= 5.2.3)
activesupport (= 5.2.3)
bundler (>= 1.3.0)
railties (= 5.0.7.2)
railties (= 5.2.3)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
@ -213,31 +233,30 @@ GEM
nokogiri (>= 1.6)
rails-html-sanitizer (1.0.4)
loofah (~> 2.2, >= 2.2.2)
railties (5.0.7.2)
actionpack (= 5.0.7.2)
activesupport (= 5.0.7.2)
railties (5.2.3)
actionpack (= 5.2.3)
activesupport (= 5.2.3)
method_source
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
thor (>= 0.19.0, < 2.0)
rainbow (3.0.0)
rake (12.3.2)
random_password (0.1.1)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
recaptcha (4.14.0)
recaptcha (5.0.0)
json
redcarpet (3.4.0)
redis (3.3.5)
remote_syslog_logger (1.0.4)
syslog_protocol
rolify (5.2.0)
rspec-core (3.8.0)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.3)
rspec-expectations (3.8.4)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.0)
rspec-mocks (3.8.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-rails (3.8.2)
@ -248,31 +267,20 @@ GEM
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.0)
rubocop (0.70.0)
rspec-support (3.8.2)
rubocop (0.72.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
rainbow (>= 2.2.2, < 4.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 1.7)
ruby-progressbar (1.10.0)
ruby-progressbar (1.10.1)
safe_yaml (1.0.5)
sass (3.7.4)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sass-rails (5.0.7)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sassc (2.0.1)
ffi (~> 1.9)
rake
sassc-rails (2.1.1)
sassc-rails (2.1.2)
railties (>= 4.0.0)
sassc (>= 2.0)
sprockets (> 3.0)
@ -285,8 +293,7 @@ GEM
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
spring (2.0.2)
activesupport (>= 4.2)
spring (2.1.0)
spring-watcher-listen (2.0.1)
listen (>= 2.7, < 4.0)
spring (>= 1.2, < 3.0)
@ -299,14 +306,12 @@ GEM
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
syslog_protocol (0.9.2)
tabler-rubygem (0.1.4)
autoprefixer-rails (>= 6.0.3)
term-ansicolor (1.7.1)
tins (~> 1.0)
thor (0.20.3)
thread_safe (0.3.6)
tilt (2.0.9)
tins (1.20.2)
tins (1.21.0)
turbolinks (5.2.0)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
@ -320,13 +325,13 @@ GEM
activemodel (>= 5.0)
bindex (>= 0.4.0)
railties (>= 5.0)
webmock (3.5.1)
webmock (3.6.0)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
websocket-driver (0.6.5)
hashdiff (>= 0.4.0, < 2.0.0)
websocket-driver (0.7.1)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
websocket-extensions (0.1.4)
xml-simple (1.1.5)
PLATFORMS
@ -337,6 +342,7 @@ DEPENDENCIES
bcrypt (~> 3.1.7)
bigbluebutton-api-ruby
bn-ldap-authentication!
bootsnap (>= 1.1.0)
bootstrap (~> 4.3.1)
byebug
cancancan (~> 2.0)
@ -361,24 +367,24 @@ DEPENDENCIES
pagy
pg (~> 0.18)
puma (~> 3.0)
rails (~> 5.0.7)
rails (~> 5.2.3)
rails-controller-testing
random_password
recaptcha
redcarpet
redis (~> 3.0)
remote_syslog_logger
rolify
rspec-rails (~> 3.7)
rubocop
sass-rails (~> 5.0)
sassc-rails
shoulda-matchers (~> 3.1)
spring
spring-watcher-listen (~> 2.0.0)
sqlite3 (~> 1.3.6)
tabler-rubygem
tabler-rubygem!
turbolinks (~> 5)
tzinfo-data
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
webmock

View File

@ -43,9 +43,9 @@ $(document).on('turbolinks:load', function(){
window.location.replace(url);
})
/* COLOR SELECTORS */
}
if (controller == "admins" && action == "site_settings") {
loadColourSelectors()
}

View File

@ -0,0 +1,41 @@
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
//
// Copyright (c) 2018 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.
//
// BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
// Handle changing of settings tabs.
$(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if (controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Handle recording emails.
$('.email-link').each(function(){
$(this).click(function(){
var subject = $(".username").text() + " " + t('room.mailer.subject');
var body = t('room.mailer.body') + "\n\n" + $(this).attr("data-pres-link");
var autogenerated = "\n\n" + t('room.mailer.autogenerated') + "\n";
var footer = t('room.mailer.footer');
var url = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) + encodeURIComponent(autogenerated) + encodeURIComponent(footer);
var win = window.open(url, '_blank');
win.focus();
});
});
}
});

View File

@ -18,7 +18,10 @@ $(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "show" || controller == "rooms" && action == "update" || controller == "users" && action == "recordings"){
if(controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Set a room header rename event
var configure_room_header = function(room_title){

View File

@ -39,29 +39,18 @@ $(document).on('turbolinks:load', function(){
}, 2000)
}
});
// Handle recording emails.
$('.email-link').each(function(){
$(this).click(function(){
var subject = $(".username").text() + " " + t('room.mailer.subject');
var body = t('room.mailer.body') + "\n\n" + $(this).attr("data-pres-link");
var autogenerated = "\n\n" + t('room.mailer.autogenerated') + "\n";
var footer = t('room.mailer.footer');
var url = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) + encodeURIComponent(autogenerated) + encodeURIComponent(footer);
var win = window.open(url, '_blank');
win.focus();
});
});
}
// Display and update all fields related to creating a room in the createRoomModal
$("#create-room-block").click(function(){
$("#create-room-name").val("")
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
$("#createRoomModal form").attr("action", $("body").data('relative-root'))
updateDropdown($(".dropdown-item[value='default']"))
$("#room_mute_on_join").prop("checked", false)
$("#room_anyone_can_start").prop("checked", false)
//show all elements & their children with a create-only class
$(".create-only").each(function() {
@ -95,6 +84,16 @@ $(document).on('turbolinks:load', function(){
})
updateCurrentSettings($(this).closest("#room-block").data("room-settings"))
accessCode = $(this).closest("#room-block").data("room-access-code")
if(accessCode){
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code") %>: " + accessCode)
$("#room_access_code").val(accessCode)
} else{
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
}
})
//Update the createRoomModal to show the correct current settings
@ -106,6 +105,12 @@ $(document).on('turbolinks:load', function(){
$("#room_mute_on_join").prop("checked", false)
}
if(settings.anyoneCanStart){
$("#room_anyone_can_start").prop("checked", true)
} else { //default option
$("#room_anyone_can_start").prop("checked", false)
}
//set dropdown value
if (settings.joinViaHtml5) {
updateDropdown($(".dropdown-item[value='html5']"))
@ -122,3 +127,21 @@ function updateDropdown(element) {
$("#dropdown-trigger").text(element.text())
$("#room_client").val(element.val())
}
function generateAccessCode(){
const accessCodeLength = 6
var validCharacters = "0123456789"
var accessCode = ""
for( var i = 0; i < accessCodeLength; i++){
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
}
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code") %>: " + accessCode)
$("#room_access_code").val(accessCode)
}
function ResetAccessCode(){
$("#create-room-access-code").text("<%= I18n.t("modal.create_room.access_code_placeholder") %>")
$("#room_access_code").val(null)
}

View File

@ -22,7 +22,8 @@ $(document).on('turbolinks:load', function(){
(controller == "rooms" && action == "show") ||
(controller == "rooms" && action == "update") ||
(controller == "rooms" && action == "join") ||
(controller == "users" && action == "recordings")) {
(controller == "users" && action == "recordings") ||
(controller == "admins" && action == "server_recordings")) {
// Submit search if the user hits enter
$("#search-input").keypress(function(key) {
var keyPressed = key.which

View File

@ -20,7 +20,7 @@ $(document).on('turbolinks:load', function(){
var action = $("body").data('action');
// Only run on the settings page.
if ((controller == "users" && action == "edit") || (controller == "users" && action == "update") || (controller == "admins" && action == "index")){
if ((controller == "users" && action == "edit") || (controller == "users" && action == "update")){
var settingsButtons = $('.setting-btn');
var settingsViews = $('.setting-view');

View File

@ -18,7 +18,10 @@ $(document).on('turbolinks:load', function(){
var controller = $("body").data('controller');
var action = $("body").data('action');
if(controller == "rooms" && action == "show" || controller == "rooms" && action == "update" || controller == "users" && action == "recordings"){
if(controller == "rooms" && action == "show"
|| controller == "rooms" && action == "update"
|| controller == "users" && action == "recordings"
|| controller == "admins" && action == "server_recordings"){
// Choose active header
// (Name, Length or Users)

View File

@ -65,3 +65,7 @@
background-color: rgba(0, 0, 0, 0.04);
}
}
.allow-icon-click{
pointer-events: auto;
}

View File

@ -84,6 +84,7 @@ a {
color: #6e7687 !important;
&:hover {
color: $primary-color !important;
background-color: $primary-color-lighten !important;
}
&:active {
background-color: $primary-color-lighten !important;
@ -130,6 +131,7 @@ input:focus, select:focus {
}
& a {
color: $primary-color !important;
border-color: $primary-color !important;
}

View File

@ -20,10 +20,11 @@ class AdminsController < ApplicationController
include Pagy::Backend
include Themer
include Emailer
include Recorder
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve]
site_settings = [:branding, :coloring, :coloring_lighten, :coloring_darken,
:registration_method, :room_authentication, :room_limit]
:registration_method, :room_authentication, :room_limit, :default_recording_visibility]
authorize_resource class: false
before_action :find_user, only: manage_users
@ -40,11 +41,27 @@ class AdminsController < ApplicationController
@pagy, @users = pagy(user_list)
end
# GET /admins/site_settings
def site_settings
end
# GET /admins/server_recordings
def server_recordings
server_rooms = if Rails.configuration.loadbalanced_configuration
Room.includes(:owner).where(users: { provider: user_settings_provider }).pluck(:bbb_id)
else
Room.pluck(:bbb_id)
end
@search, @order_column, @order_direction, recs =
all_recordings(server_rooms, @user_domain, params.permit(:search, :column, :direction), true, true)
@pagy, @recordings = pagy_array(recs)
end
# MANAGE USERS
# GET /admins/edit/:user_uid
def edit_user
render "admins/index", locals: { setting_id: "account" }
end
# POST /admins/promote/:user_uid
@ -111,7 +128,7 @@ class AdminsController < ApplicationController
# POST /admins/branding
def branding
@settings.update_value("Branding Image", params[:url])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/color
@ -119,23 +136,23 @@ class AdminsController < ApplicationController
@settings.update_value("Primary Color", params[:color])
@settings.update_value("Primary Color Lighten", color_lighten(params[:color]))
@settings.update_value("Primary Color Darken", color_darken(params[:color]))
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
def coloring_lighten
@settings.update_value("Primary Color Lighten", params[:color])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
def coloring_darken
@settings.update_value("Primary Color Darken", params[:color])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/room_authentication
def room_authentication
@settings.update_value("Room Authentication", params[:value])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/registration_method/:method
@ -144,11 +161,11 @@ class AdminsController < ApplicationController
# Only allow change to Join by Invitation if user has emails enabled
if !Rails.configuration.enable_email_verification && new_method == Rails.configuration.registration_methods[:invite]
redirect_to admins_path,
redirect_to admin_site_settings_path,
flash: { alert: I18n.t("administrator.flash.invite_email_verification") }
else
@settings.update_value("Registration Method", new_method)
redirect_to admins_path,
redirect_to admin_site_settings_path,
flash: { success: I18n.t("administrator.flash.registration_method_updated") }
end
end
@ -156,13 +173,20 @@ class AdminsController < ApplicationController
# POST /admins/room_limit
def room_limit
@settings.update_value("Room Limit", params[:limit])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") }
redirect_to admin_site_settings_path, flash: { success: I18n.t("administrator.flash.settings") }
end
# POST /admins/default_recording_visibility
def default_recording_visibility
@settings.update_value("Default Recording Visibility", params[:visibility])
redirect_to admins_path, flash: { success: I18n.t("administrator.flash.settings") + ". " +
I18n.t("administrator.site_settings.recording_visibility.warning") }
end
private
def find_user
@user = User.find_by!(uid: params[:user_uid])
@user = User.where(uid: params[:user_uid]).includes(:roles).first
end
def find_setting
@ -176,21 +200,19 @@ class AdminsController < ApplicationController
# Gets the list of users based on your configuration
def user_list
initial_list = if current_user.has_role? :super_admin
User.where.not(id: current_user.id)
initial_list = if current_user.has_cached_role? :super_admin
User.where.not(id: current_user.id).includes(:roles)
else
User.without_role(:super_admin).where.not(id: current_user.id)
User.without_role(:super_admin).where.not(id: current_user.id).includes(:roles)
end
list = @role.present? ? initial_list.with_role(@role.to_sym) : initial_list
if Rails.configuration.loadbalanced_configuration
list.where(provider: user_settings_provider)
.admins_search(@search)
.admins_order(@order_column, @order_direction)
initial_list.where(provider: user_settings_provider)
.admins_search(@search, @role)
.admins_order(@order_column, @order_direction)
else
list.admins_search(@search)
.admins_order(@order_column, @order_direction)
initial_list.admins_search(@search, @role)
.admins_order(@order_column, @order_direction)
end
end

View File

@ -121,6 +121,9 @@ class ApplicationController < ActionController::Base
meeting_logout_url: request.base_url + logout_room_path(@room),
meeting_recorded: true,
moderator_message: "#{invite_msg}\n\n#{request.base_url + room_path(@room)}",
host: request.host,
recording_default_visibility: Setting.find_or_create_by!(provider: user_settings_provider)
.get_value("Default Recording Visibility") == "public"
}
end
@ -131,7 +134,7 @@ class ApplicationController < ActionController::Base
# Checks to make sure that the admin has changed his password from the default
def check_admin_password
if current_user&.has_role?(:admin) && current_user&.greenlight_account? &&
if current_user&.has_cached_role?(:admin) && current_user&.greenlight_account? &&
current_user&.authenticate(Rails.configuration.admin_password_default)
flash.now[:alert] = I18n.t("default_admin",
@ -179,10 +182,10 @@ class ApplicationController < ActionController::Base
# Checks if the user is banned and logs him out if he is
def check_user_role
if current_user&.has_role? :denied
if current_user&.has_cached_role? :denied
session.delete(:user_id)
redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") }
elsif current_user&.has_role? :pending
elsif current_user&.has_cached_role? :pending
session.delete(:user_id)
redirect_to root_path, flash: { alert: I18n.t("registration.approval.fail") }
end

View File

@ -64,13 +64,19 @@ module Emailer
def send_approval_user_signup_email(user)
return unless Rails.configuration.enable_email_verification
UserMailer.approval_user_signup(user, admins_url, logo_image, user_color, admin_emails).deliver_now
admin_emails = admin_emails()
unless admin_emails.empty?
UserMailer.approval_user_signup(user, admins_url, logo_image, user_color, admin_emails).deliver_now
end
end
def send_invite_user_signup_email(user)
return unless Rails.configuration.enable_email_verification
UserMailer.invite_user_signup(user, admins_url, logo_image, user_color, admin_emails).deliver_now
admin_emails = admin_emails()
unless admin_emails.empty?
UserMailer.invite_user_signup(user, admins_url, logo_image, user_color, admin_emails).deliver_now
end
end
private

View File

@ -16,14 +16,53 @@
# You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
module APIConcern
module Recorder
extend ActiveSupport::Concern
include ::BbbApi
# Fetches all recordings for a room.
def recordings(room_bbb_id, provider, search_params = {}, ret_search_params = false)
res = bbb(provider).get_recordings(meetingID: room_bbb_id)
format_recordings(res, search_params, ret_search_params)
end
# Fetches a rooms public recordings.
def public_recordings(room_bbb_id, provider, search_params = {}, ret_search_params = false)
search, order_col, order_dir, recs = recordings(room_bbb_id, provider, search_params, ret_search_params)
[search, order_col, order_dir, recs.select { |r| r[:metadata][:"gl-listed"] == "true" }]
end
# Makes paginated API calls to get recordings
def all_recordings(room_bbb_ids, provider, search_params = {}, ret_search_params = false, search_name = false)
pag_num = Rails.configuration.pagination_number
pag_loops = room_bbb_ids.length / pag_num - 1
res = { recordings: [] }
(0..pag_loops).each do |i|
pag_rooms = room_bbb_ids[pag_num * i, pag_num]
# bbb.get_recordings returns an object
# take only the array portion of the object that is returned
full_res = bbb(provider).get_recordings(meetingID: pag_rooms)
res[:recordings].push(*full_res[:recordings])
end
last_pag_room = room_bbb_ids[pag_num * (pag_loops + 1), room_bbb_ids.length % pag_num]
full_res = bbb(provider).get_recordings(meetingID: last_pag_room)
res[:recordings].push(*full_res[:recordings])
format_recordings(res, search_params, ret_search_params, search_name)
end
# Format, filter, and sort recordings to match their current use in the app
def format_recordings(api_res, search_params, ret_search_params)
def format_recordings(api_res, search_params, ret_search_params, search_name = false)
search = search_params[:search] || ""
order_col = search_params[:column] && search_params[:direction] != "none" ? search_params[:column] : "end_time"
order_dir = search_params[:column] && search_params[:direction] != "none" ? search_params[:direction] : "asc"
order_dir = search_params[:column] && search_params[:direction] != "none" ? search_params[:direction] : "desc"
search = search.downcase
@ -40,7 +79,7 @@ module APIConcern
r.delete(:playback)
end
recs = filter_recordings(api_res, search)
recs = filter_recordings(api_res, search, search_name)
recs = sort_recordings(recs, order_col, order_dir)
if ret_search_params
@ -50,7 +89,7 @@ module APIConcern
end
end
def filter_recordings(api_res, search)
def filter_recordings(api_res, search, search_name = false)
api_res[:recordings].select do |r|
(!r[:metadata].nil? && ((!r[:metadata][:name].nil? &&
r[:metadata][:name].downcase.include?(search)) ||
@ -59,33 +98,34 @@ module APIConcern
((r[:metadata].nil? || r[:metadata][:name].nil?) &&
r[:name].downcase.include?(search)) ||
r[:participants].include?(search) ||
!r[:playbacks].select { |p| p[:type].downcase.include?(search) }.empty?
!r[:playbacks].select { |p| p[:type].downcase.include?(search) }.empty? ||
(search_name && Room.find_by(bbb_id: r[:meetingID]).owner.email.downcase.include?(search))
end
end
def sort_recordings(recs, order_col, order_dir)
recs = case order_col
when "end_time"
recs.sort_by { |r| r[:endTime] }
recs.sort_by { |r| r[:endTime] }
when "name"
recs.sort_by do |r|
if !r[:metadata].nil? && !r[:metadata][:name].nil?
r[:metadata][:name].downcase
else
r[:name].downcase
end
end
recs.sort_by do |r|
if !r[:metadata].nil? && !r[:metadata][:name].nil?
r[:metadata][:name].downcase
else
r[:name].downcase
end
end
when "length"
recs.sort_by { |r| r[:playbacks].reject { |p| p[:type] == "statistics" }.first[:length] }
recs.sort_by { |r| r[:playbacks].reject { |p| p[:type] == "statistics" }.first[:length] }
when "users"
recs.sort_by { |r| r[:participants] }
recs.sort_by { |r| r[:participants] }
when "visibility"
recs.sort_by { |r| r[:metadata][:"gl-listed"] }
recs.sort_by { |r| r[:metadata][:"gl-listed"] }
when "formats"
recs.sort_by { |r| r[:playbacks].first[:type].downcase }
else
recs.sort_by { |r| r[:endTime] }
end
recs.sort_by { |r| r[:playbacks].first[:type].downcase }
else
recs.sort_by { |r| r[:endTime] }
end
if order_dir == 'asc'
recs

View File

@ -24,7 +24,7 @@ module Themer
# Uses the built in Sass Engine to lighten the color
dummy_scss = "h1 { color: $lighten; }"
compiled = Sass::Engine.new("$lighten:lighten(#{color}, 40%);" + dummy_scss, syntax: :scss).render
compiled = SassC::Engine.new("$lighten:lighten(#{color}, 40%);" + dummy_scss, syntax: :scss).render
string_locater = 'color: '
color_start = compiled.index(string_locater) + string_locater.length
@ -37,7 +37,7 @@ module Themer
# Uses the built in Sass Engine to darken the color
dummy_scss = "h1 { color: $darken; }"
compiled = Sass::Engine.new("$darken:darken(#{color}, 10%);" + dummy_scss, syntax: :scss).render
compiled = SassC::Engine.new("$darken:darken(#{color}, 10%);" + dummy_scss, syntax: :scss).render
string_locater = 'color: '
color_start = compiled.index(string_locater) + string_locater.length

View File

@ -50,6 +50,11 @@ class RecordingsController < ApplicationController
# Ensure the user is logged into the room they are accessing.
def verify_room_ownership
redirect_to root_path unless @room.owned_by?(current_user)
if !current_user ||
!@room.owned_by?(current_user) ||
!current_user.has_cached_role?(:admin) ||
!current_user.has_cached_role?(:super_admin)
redirect_to root_path
end
end
end

View File

@ -19,12 +19,13 @@
class RoomsController < ApplicationController
include RecordingsHelper
include Pagy::Backend
include Recorder
before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
before_action :validate_verified_email, except: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
before_action :find_room, except: :create
before_action :verify_room_ownership, except: [:create, :show, :join, :logout]
before_action :verify_room_ownership, except: [:create, :show, :join, :logout, :login]
before_action :verify_room_owner_verified, only: [:show, :join],
unless: -> { !Rails.configuration.enable_email_verification }
before_action :verify_user_not_admin, only: [:show]
@ -35,9 +36,10 @@ class RoomsController < ApplicationController
return redirect_to current_user.main_room, flash: { alert: I18n.t("room.room_limit") } if room_limit_exceeded
@room = Room.new(name: room_params[:name])
@room = Room.new(name: room_params[:name], access_code: room_params[:access_code])
@room.owner = current_user
@room.room_settings = create_room_settings_string(room_params[:mute_on_join], room_params[:client])
@room.room_settings = create_room_settings_string(room_params[:mute_on_join], room_params[:client],
room_params[:anyone_can_start])
if @room.save
if room_params[:auto_join] == "1"
@ -54,13 +56,15 @@ class RoomsController < ApplicationController
# GET /:room_uid
def show
@is_running = @room.running?
@anyone_can_start = JSON.parse(@room[:room_settings])["anyoneCanStart"]
if current_user && @room.owned_by?(current_user)
@search, @order_column, @order_direction, recs =
@room.recordings(params.permit(:search, :column, :direction), true)
recordings(@room.bbb_id, @user_domain, params.permit(:search, :column, :direction), true)
@pagy, @recordings = pagy_array(recs)
@is_running = @room.running?
else
# Get users name
@name = if current_user
@ -72,7 +76,7 @@ class RoomsController < ApplicationController
end
@search, @order_column, @order_direction, pub_recs =
@room.public_recordings(params.permit(:search, :column, :direction), true)
public_recordings(@room.bbb_id, @user_domain, params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
@ -105,6 +109,12 @@ class RoomsController < ApplicationController
opts = default_meeting_options
unless @room.owned_by?(current_user)
# Don't allow users to join unless they have a valid access code or the room doesn't
# have an access code
if @room.access_code && !@room.access_code.empty? && @room.access_code != session[:access_code]
return redirect_to room_path(room_uid: params[:room_uid]), flash: { alert: I18n.t("room.access_code_required") }
end
# Assign join name if passed.
if params[@room.invite_path]
@join_name = params[@room.invite_path][:join_name]
@ -117,31 +127,7 @@ class RoomsController < ApplicationController
# create or update cookie with join name
cookies.encrypted[:greenlight_name] = @join_name unless cookies.encrypted[:greenlight_name] == @join_name
if @room.running? || @room.owned_by?(current_user)
# Determine if the user needs to join as a moderator.
opts[:user_is_moderator] = @room.owned_by?(current_user)
# Check if the user has specified which client to use
room_settings = JSON.parse(@room[:room_settings])
opts[:join_via_html5] = room_settings["joinViaHtml5"] if room_settings["joinViaHtml5"]
if current_user
redirect_to @room.join_path(current_user.name, opts, current_user.uid)
else
join_name = params[:join_name] || params[@room.invite_path][:join_name]
redirect_to @room.join_path(join_name, opts)
end
else
search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs =
@room.public_recordings(search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
# They need to wait until the meeting begins.
render :wait
end
join_room(opts)
end
# DELETE /:room_uid
@ -184,6 +170,8 @@ class RoomsController < ApplicationController
update_room_attributes("settings")
# Update the rooms name if it has been changed
update_room_attributes("name") if @room.name != room_params[:name]
# Update the room's access code if it has changed
update_room_attributes("access_code") if @room.access_code != room_params[:access_code]
rescue StandardError
flash[:alert] = I18n.t("room.update_settings_error")
else
@ -198,6 +186,15 @@ class RoomsController < ApplicationController
redirect_to @room
end
# POST /:room_uid/login
def login
session[:access_code] = room_params[:access_code]
flash[:alert] = I18n.t("room.access_code_required") if session[:access_code] != @room.access_code
redirect_to room_path(@room.uid)
end
private
def update_room_attributes(update_type)
@ -205,13 +202,16 @@ class RoomsController < ApplicationController
if update_type.eql? "name"
@room.update_attributes(name: params[:room_name] || room_params[:name])
elsif update_type.eql? "settings"
room_settings_string = create_room_settings_string(room_params[:mute_on_join], room_params[:client])
room_settings_string = create_room_settings_string(room_params[:mute_on_join], room_params[:client],
room_params[:anyone_can_start])
@room.update_attributes(room_settings: room_settings_string)
elsif update_type.eql? "access_code"
@room.update_attributes(access_code: room_params[:access_code])
end
end
end
def create_room_settings_string(mute_res, client_res)
def create_room_settings_string(mute_res, client_res, start_res)
room_settings = {}
room_settings["muteOnStart"] = mute_res == "1"
@ -221,11 +221,13 @@ class RoomsController < ApplicationController
room_settings["joinViaHtml5"] = false
end
room_settings["anyoneCanStart"] = start_res == "1"
room_settings.to_json
end
def room_params
params.require(:room).permit(:name, :auto_join, :mute_on_join, :client)
params.require(:room).permit(:name, :auto_join, :mute_on_join, :client, :access_code, :anyone_can_start)
end
# Find the room from the uid.
@ -274,7 +276,7 @@ class RoomsController < ApplicationController
end
def verify_user_not_admin
redirect_to admins_path if current_user && current_user&.has_role?(:super_admin)
redirect_to admins_path if current_user && current_user&.has_cached_role?(:super_admin)
end
def auth_required
@ -287,8 +289,38 @@ class RoomsController < ApplicationController
# Does not apply to admin
# 15+ option is used as unlimited
return false if current_user&.has_role?(:admin) || limit == 15
return false if current_user&.has_cached_role?(:admin) || limit == 15
current_user.rooms.count >= limit
end
def join_room(opts)
room_settings = JSON.parse(@room[:room_settings])
if @room.running? || @room.owned_by?(current_user) || room_settings["anyoneCanStart"]
# Determine if the user needs to join as a moderator.
opts[:user_is_moderator] = @room.owned_by?(current_user) ||
(room_settings["anyoneCanStart"] && !@room.running?)
# Check if the user has specified which client to use
opts[:join_via_html5] = room_settings["joinViaHtml5"] if room_settings["joinViaHtml5"]
if current_user
redirect_to @room.join_path(current_user.name, opts, current_user.uid)
else
join_name = params[:join_name] || params[@room.invite_path][:join_name]
redirect_to @room.join_path(join_name, opts)
end
else
search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs =
public_recordings(@room.bbb_id, @user_domain, search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs)
# They need to wait until the meeting begins.
render :wait
end
end
end

View File

@ -30,7 +30,7 @@ class ThemesController < ApplicationController
@file_contents = File.read(file_name)
# Include the variables and covert scss file to css
@compiled = Sass::Engine.new("$primary-color:#{color};" \
@compiled = SassC::Engine.new("$primary-color:#{color};" \
"$primary-color-lighten:#{lighten_color};" \
"$primary-color-darken:#{darken_color};" +
@file_contents, syntax: :scss).render

View File

@ -21,6 +21,7 @@ class UsersController < ApplicationController
include Pagy::Backend
include Emailer
include Registrar
include Recorder
before_action :find_user, only: [:edit, :update, :destroy]
before_action :ensure_unauthenticated, only: [:new, :create]
@ -66,6 +67,12 @@ class UsersController < ApplicationController
flash[:alert] = I18n.t("registration.deprecated.new_signin")
session[:old_twitter_user_id] = params[:old_twitter_user_id] unless params[:old_twitter_user_id].nil?
end
providers = configured_providers
if (!allow_user_signup? || !allow_greenlight_accounts?) && providers.count == 1 &&
!Rails.configuration.loadbalanced_configuration
return redirect_to "#{Rails.configuration.relative_url_root}/auth/#{providers.first}"
end
end
# GET /ldap_signin
@ -103,6 +110,8 @@ class UsersController < ApplicationController
# PATCH /u/:user_uid/edit
def update
redirect_path = current_user.admin_of?(@user) ? admins_path : edit_user_path(@user)
if params[:setting] == "password"
# Update the users password.
errors = {}
@ -123,7 +132,7 @@ class UsersController < ApplicationController
if errors.empty? && @user.save
# Notify the user that their account has been updated.
flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user)
redirect_to redirect_path
else
# Append custom errors.
errors.each { |k, v| @user.errors.add(k, v) }
@ -132,11 +141,11 @@ class UsersController < ApplicationController
elsif user_params[:email] != @user.email && @user.update_attributes(user_params)
@user.update_attributes(email_verified: false)
flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user)
redirect_to redirect_path
elsif @user.update_attributes(user_params)
update_locale(@user)
flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user)
redirect_to redirect_path
else
render :edit, params: { settings: params[:settings] }
end
@ -165,7 +174,8 @@ class UsersController < ApplicationController
def recordings
if current_user && current_user.uid == params[:user_uid]
@search, @order_column, @order_direction, recs =
current_user.all_recordings(params.permit(:search, :column, :direction), true)
all_recordings(current_user.rooms.pluck(:bbb_id), current_user.provider,
params.permit(:search, :column, :direction), true)
@pagy, @recordings = pagy_array(recs)
else
redirect_to root_path
@ -185,7 +195,7 @@ class UsersController < ApplicationController
private
def find_user
@user = User.find_by!(uid: params[:user_uid])
@user = User.where(uid: params[:user_uid]).includes(:roles).first
end
def ensure_unauthenticated

View File

@ -19,6 +19,18 @@
module AdminsHelper
include Pagy::Frontend
# Returns the action method of the current page
def active_page
route = Rails.application.routes.recognize_path(request.env['PATH_INFO'])
route[:action]
end
# Gets the email of the room owner to which the recording belongs to
def recording_owner_email(room_id)
Room.find_by(bbb_id: room_id).owner.email
end
def display_invite
current_page?(admins_path) && invite_registration
end
@ -43,6 +55,15 @@ module AdminsHelper
end
end
def recording_default_visibility_string
if Setting.find_or_create_by!(provider: user_settings_provider)
.get_value("Default Recording Visibility") == "public"
I18n.t("administrator.site_settings.recording_visibility.public")
else
I18n.t("administrator.site_settings.recording_visibility.private")
end
end
def registration_method_string
case registration_method
when Rails.configuration.registration_methods[:open]

View File

@ -107,7 +107,7 @@ module ApplicationHelper
# Returns the page that the logo redirects to when clicked on
def home_page
return root_path unless current_user
return admins_path if current_user.has_role? :super_admin
return admins_path if current_user.has_cached_role? :super_admin
current_user.main_room
end
end

View File

@ -37,7 +37,7 @@ module RoomsHelper
# Does not apply to admin or users that aren't signed in
# 15+ option is used as unlimited
return false if current_user&.has_role?(:admin) || limit == 15
return false if current_user&.has_cached_role?(:admin) || limit == 15
current_user.rooms.length >= limit
end
@ -46,7 +46,7 @@ module RoomsHelper
# Get how many rooms need to be deleted to reach allowed room number
limit = Setting.find_or_create_by!(provider: user_settings_provider).get_value("Room Limit").to_i
return false if current_user&.has_role?(:admin) || limit == 15
return false if current_user&.has_cached_role?(:admin) || limit == 15
@diff = current_user.rooms.count - limit
@diff.positive? && current_user.rooms.pluck(:id).index(room.id) + 1 > limit

View File

@ -61,7 +61,17 @@ module SessionsHelper
# Retrieves the current user.
def current_user
@current_user ||= User.find_by(id: session[:user_id])
@current_user ||= User.where(id: session[:user_id]).includes(:roles).first
if Rails.configuration.loadbalanced_configuration
if @current_user && !@current_user.has_role?(:super_admin) &&
@current_user.provider != @user_domain
@current_user = nil
session.clear
end
end
@current_user
end
def generate_checksum(user_domain, redirect_url, secret)

View File

@ -31,7 +31,7 @@ module ThemingHelper
# Returns the user's provider in the settings context
def user_settings_provider
if Rails.configuration.loadbalanced_configuration && current_user && !current_user&.has_role?(:super_admin)
if Rails.configuration.loadbalanced_configuration && current_user && !current_user&.has_cached_role?(:super_admin)
current_user.provider
elsif Rails.configuration.loadbalanced_configuration
@user_domain

View File

@ -19,7 +19,6 @@
require 'bbb_api'
class Room < ApplicationRecord
include ::APIConcern
include ::BbbApi
before_create :setup
@ -40,7 +39,7 @@ class Room < ApplicationRecord
# Checks if a room is running on the BigBlueButton server.
def running?
bbb.is_meeting_running?(bbb_id)
bbb(owner.provider).is_meeting_running?(bbb_id)
end
# Determines the invite path for the room.
@ -57,12 +56,15 @@ class Room < ApplicationRecord
attendeePW: attendee_pw,
moderatorOnlyMessage: options[:moderator_message],
muteOnStart: options[:mute_on_start] || false,
"meta_#{META_LISTED}": false,
"meta_#{META_LISTED}": options[:recording_default_visibility] || false,
"meta_bbb-origin-version": Greenlight::Application::VERSION,
"meta_bbb-origin": "Greenlight",
"meta_bbb-origin-server-name": options[:host]
}
# Send the create request.
begin
meeting = bbb.create_meeting(name, bbb_id, create_options)
meeting = bbb(owner.provider).create_meeting(name, bbb_id, create_options)
# Update session info.
unless meeting[:messageKey] == 'duplicateWarning'
update_attributes(sessions: sessions + 1,
@ -85,10 +87,10 @@ class Room < ApplicationRecord
options[:user_is_moderator] ||= false
options[:meeting_recorded] ||= false
return call_invalid_res unless bbb
return call_invalid_res unless bbb(owner.provider)
# Get the meeting info.
meeting_info = bbb.get_meeting_info(bbb_id, nil)
meeting_info = bbb(owner.provider).get_meeting_info(bbb_id, nil)
# Determine the password to use when joining.
password = if options[:user_is_moderator]
@ -102,7 +104,7 @@ class Room < ApplicationRecord
join_opts[:userID] = uid if uid
join_opts[:joinViaHtml5] = options[:join_via_html5] if options[:join_via_html5]
bbb.join_meeting_url(bbb_id, name, password, join_opts)
bbb(owner.provider).join_meeting_url(bbb_id, name, password, join_opts)
end
# Notify waiting users that a meeting has started.
@ -112,7 +114,7 @@ class Room < ApplicationRecord
# Retrieves all the users in a room.
def participants
res = bbb.get_meeting_info(bbb_id, nil)
res = bbb(owner.provider).get_meeting_info(bbb_id, nil)
res[:attendees].map do |att|
User.find_by(uid: att[:userID], name: att[:fullName])
end
@ -121,27 +123,18 @@ class Room < ApplicationRecord
[]
end
# Fetches all recordings for a room.
def recordings(search_params = {}, ret_search_params = false)
res = bbb.get_recordings(meetingID: bbb_id)
format_recordings(res, search_params, ret_search_params)
end
# Fetches a rooms public recordings.
def public_recordings(search_params = {}, ret_search_params = false)
search, order_col, order_dir, recs = recordings(search_params, ret_search_params)
[search, order_col, order_dir, recs.select { |r| r[:metadata][:"gl-listed"] == "true" }]
def recording_count
bbb(owner.provider).get_recordings(meetingID: bbb_id)[:recordings].length
end
def update_recording(record_id, meta)
meta[:recordID] = record_id
bbb.send_api_request("updateRecordings", meta)
bbb(owner.provider).send_api_request("updateRecordings", meta)
end
# Deletes a recording from a room.
def delete_recording(record_id)
bbb.delete_recordings(record_id)
bbb(owner.provider).delete_recordings(record_id)
end
private
@ -156,7 +149,7 @@ class Room < ApplicationRecord
# Deletes all recordings associated with the room.
def delete_all_recordings
record_ids = recordings.map { |r| r[:recordID] }
record_ids = bbb(owner.provider).get_recordings(meetingID: bbb_id)[:recordings].pluck(:recordID)
delete_recording(record_ids) unless record_ids.empty?
end

View File

@ -20,7 +20,6 @@ require 'bbb_api'
class User < ApplicationRecord
rolify
include ::APIConcern
include ::BbbApi
attr_accessor :reset_token
@ -119,7 +118,7 @@ class User < ApplicationRecord
end
end
def self.admins_search(string)
def self.admins_search(string, role)
active_database = Rails.configuration.database_configuration[Rails.env]["adapter"]
# Postgres requires created_at to be cast to a string
created_at_query = if active_database == "postgresql"
@ -128,38 +127,29 @@ class User < ApplicationRecord
"created_at"
end
search_query = "users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
" OR users.#{created_at_query} LIKE :search OR provider LIKE :search"
search_query = ""
role_search_param = ""
if role.present?
search_query = "(users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
" OR users.#{created_at_query} LIKE :search OR provider LIKE :search)" \
" AND roles.name = :roles_search"
role_search_param = role
else
search_query = "users.name LIKE :search OR email LIKE :search OR username LIKE :search" \
" OR users.#{created_at_query} LIKE :search OR provider LIKE :search" \
" OR roles.name LIKE :roles_search"
role_search_param = "%#{string}%".downcase
end
search_param = "%#{string}%"
where(search_query, search: search_param)
joins("LEFT OUTER JOIN users_roles ON users_roles.user_id = users.id LEFT OUTER JOIN roles " \
"ON roles.id = users_roles.role_id").distinct
.where(search_query, search: search_param, roles_search: role_search_param)
end
def self.admins_order(column, direction)
order("#{column} #{direction}")
end
def all_recordings(search_params = {}, ret_search_params = false)
pag_num = Rails.configuration.pagination_number
pag_loops = rooms.length / pag_num - 1
res = { recordings: [] }
(0..pag_loops).each do |i|
pag_rooms = rooms[pag_num * i, pag_num]
# bbb.get_recordings returns an object
# take only the array portion of the object that is returned
full_res = bbb.get_recordings(meetingID: pag_rooms.pluck(:bbb_id))
res[:recordings].push(*full_res[:recordings])
end
last_pag_room = rooms[pag_num * (pag_loops + 1), rooms.length % pag_num]
full_res = bbb.get_recordings(meetingID: last_pag_room.pluck(:bbb_id))
res[:recordings].push(*full_res[:recordings])
format_recordings(res, search_params, ret_search_params)
# Arel.sql to avoid sql injection
order(Arel.sql("#{column} #{direction}"))
end
# Activates an account and initialize a users main room
@ -231,13 +221,17 @@ class User < ApplicationRecord
def admin_of?(user)
if Rails.configuration.loadbalanced_configuration
if has_role? :super_admin
# Pulls in the user roles if they weren't request in the original request
# So the has_cached_role? doesn't always return false
user.roles
if has_cached_role? :super_admin
id != user.id
else
(has_role? :admin) && (id != user.id) && (provider == user.provider) && (!user.has_role? :super_admin)
(has_cached_role? :admin) && (id != user.id) && (provider == user.provider) &&
(!user.has_cached_role? :super_admin)
end
else
((has_role? :admin) || (has_role? :super_admin)) && (id != user.id)
((has_cached_role? :admin) || (has_cached_role? :super_admin)) && (id != user.id)
end
end

View File

@ -0,0 +1,26 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="list-group list-group-transparent mb-0">
<%= link_to admins_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "index"}" do %>
<span class="icon mr-3"><i class="fas fa-users"></i></span><%= t("administrator.users.title") %>
<% end %>
<%= link_to admin_site_settings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "site_settings"}" do %>
<span class="icon mr-4"><i class="fas fa-cogs"></i></span><%= t("administrator.site_settings.title") %>
<% end %>
<%= link_to admin_recordings_path, class: "list-group-item list-group-item-action dropdown-item #{"active" if active_page == "server_recordings"}" do %>
<span class="icon mr-4"><i class="fas fa-video"></i></i></span><%= t("administrator.recordings.title") %>
<% end %>
</div>

View File

@ -0,0 +1,91 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="row">
<div class="col-12">
<div class="table-responsive">
<table id="recordings-table" class="table table-hover table-outline table-vcenter text-nowrap card-table">
<thead>
<tr>
<th data-header="name" data-order="<%= @order_column == "name" ? @order_direction : "none" %>">
<%= t("recording.table.name") %>
<% if @order_column == "name" && @order_direction == "desc" %>
<% elsif @order_column == "name" && @order_direction == "asc" %>
<% end %>
</th>
<th class="text-left" data-header="length" data-order="<%= @order_column == "length" ? @order_direction : "none" %>">
<%= t("recording.table.length") %>
<% if @order_column == "length" && @order_direction == "desc" %>
<% elsif @order_column == "length" && @order_direction == "asc" %>
<% end %>
</th>
<th class="text-left" data-header="users" data-order="<%= @order_column == "users" ? @order_direction : "none" %>">
<%= t("recording.table.users") %>
<% if @order_column == "users" && @order_direction == "desc" %>
<% elsif @order_column == "users" && @order_direction == "asc" %>
<% end %>
</th>
<th class="text-left" data-header="visibility" data-order="<%= @order_column == "visibility" ? @order_direction : "none" %>">
<%= t("recording.table.visibility") %>
<% if @order_column == "visibility" && @order_direction == "desc" %>
<% elsif @order_column == "visibility" && @order_direction == "asc" %>
<% end %>
</th>
<th data-header="formats" data-order="<%= @order_column == "formats" ? @order_direction : "none" %>">
<%= t("recording.table.formats") %>
<% if @order_column == "formats" && @order_direction == "desc" %>
<% elsif @order_column == "formats" && @order_direction == "asc" %>
<% end %>
</th>
<th class="text-center"><i class="icon-settings"></i></th>
</tr>
</thead>
<tbody id="recording-table">
<tr id="no_recordings_found" style="display: none;">
<td colspan="7" class="text-center h4 p-6 font-weight-normal" >
<%= t("recording.no_matched_recordings", inject:"") %>
</td>
</tr>
<% if @recordings.empty? %>
<tr>
<td colspan="7" class="text-center h4 p-6 font-weight-normal">
<%= t("administrator.recordings.no_recordings") %>
</td>
</tr>
<% else %>
<% @recordings.each do |recording| %>
<%= render "admins/components/server_recording_row", recording: recording %>
<% end %>
<% end %>
</tbody>
</table>
<% if !@recordings.empty?%>
<div class="float-right mr-4 mt-4">
<%== pagy_bootstrap_nav(@pagy) %>
</div>
<% end %>
</div>
</div>
</div>

View File

@ -0,0 +1,81 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<tr>
<td>
<div id="recording-title" class="form-inline edit_hover_class" data-recordid="<%= recording[:recordID] %>" data-room-uid="<%= room_uid_from_bbb(recording[:meetingID]) %>" data-path="<%= update_room_path(room_uid: room_uid_from_bbb(recording[:meetingID])) %>">
<text id='recording-text'>
<% if recording[:metadata][:name] %>
<%= recording[:metadata][:name] %>
<% else %>
<%= recording[:name] %>
<% end %>
</text>
<a><i id="edit-record" class="fa fa-edit align-top ml-2" data-edit-recordid="<%= recording[:recordID] %>"></i></a>
</div>
<div class="small text-muted">
<%= t("recording.recorded_on", date: recording_date(recording[:startTime])) %>
</div>
<div class="small text-muted">
<%= recording_owner_email(recording[:meetingID]) %>
</div>
</td>
<td id="recording-length" class="text-left" data-full-length="<%= recording[:playbacks].empty? ? 0 : recording[:playbacks].first[:length]%>">
<%= recording_length(recording[:playbacks]) %>
</td>
<td id="recording-users" class="text-left">
<%= recording[:participants] || "-" %>
</td>
<td class="text-left">
<div class="dropdown">
<% if recording[:metadata][:"gl-listed"] == "true" %>
<button class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown"><i class="dropdown-icon fas fa-globe px-2"></i> <%= t("recording.visibility.public") %></button>
<% else %>
<button class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown"><i class="dropdown-icon fas fa-link px-2"></i> <%= t("recording.visibility.unlisted") %></button>
<% end %>
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "public"), class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-globe"></i> <%= t("recording.visibility.public") %>
<% end %>
<%= button_to update_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID], state: "unlisted"), class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-link"></i> <%= t("recording.visibility.unlisted") %>
<% end %>
</div>
</div>
</td>
<td>
<% sorted_formats = recording[:playbacks].sort_by! { |p| p[:type] } %>
<% sorted_formats.each do |p| %>
<%= link_to t("recording.format.#{p[:type]}"), p[:url], class: "btn btn-sm btn-primary", target: "_blank" %>
<% end %>
</td>
<td class="text-center">
<div class="item-action dropdown">
<a href="javascript:void(0)" data-toggle="dropdown" class="icon">
<i class="fas fa-ellipsis-v px-4"></i>
</a>
<div class="dropdown-menu dropdown-menu-right">
<% p = recording[:playbacks].find do |p| p.key?(:length) end %>
<% if p %>
<a class="dropdown-item email-link" data-pres-link="<%= p[:url] %>"><i class="dropdown-icon far fa-envelope"></i> <%= t("recording.email") %></a>
<div class="dropdown-divider"></div>
<% end %>
<%= button_to delete_recording_path(meetingID: recording[:meetingID], record_id: recording[:recordID]), method: :delete, class: "dropdown-item" do %>
<i class="dropdown-icon far fa-trash-alt"></i> <%= t("delete") %>
<% end %>
</div>
</div>
</td>
</tr>

View File

@ -0,0 +1,26 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= content_tag(:div, id: setting_id, class: "setting-view card") do %>
<div class="card-body p-6">
<div class="card-title text-primary">
<div class="form-group">
<%= render "shared/components/subtitle", subtitle: setting_title, search: search %>
</div>
</div>
<%= render "admins/components/#{setting_id}" %>
</div>
<% end %>

View File

@ -98,6 +98,27 @@
</div>
</div>
</div>
</div>
<div class="mb-6 row">
<div class="col-12">
<div class="form-group">
<label class="form-label"><%= t("administrator.site_settings.recording_visibility.title") %></label>
<label class="form-label text-muted"><%= t("administrator.site_settings.recording_visibility.info") %></label>
<div class="dropdown">
<button class="btn btn-primary dropdown-toggle" type="button" id="room-auth" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= recording_default_visibility_string %>
</button>
<div class="dropdown-menu" aria-labelledby="room-auth">
<%= button_to admin_recording_visibility_path(visibility: "public"), class: "dropdown-item" do %>
<%= t("administrator.site_settings.recording_visibility.public") %>
<% end %>
<%= button_to admin_recording_visibility_path(visibility: "private"), class: "dropdown-item" do %>
<%= t("administrator.site_settings.recording_visibility.private") %>
<% end %>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">

View File

@ -13,6 +13,21 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<% if @role.present? %>
<%= render "shared/components/admins_tags" %>
<% end %>
@ -146,3 +161,4 @@
</div>
<%= render "shared/modals/invite_user_modal" %>

View File

@ -0,0 +1,27 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="container pt-6">
<%= render "shared/components/subtitle", subtitle: t("administrator.title"), search: false %>
<div class="row">
<div class="col-lg-3 mb-4">
<%= render "admins/components/menu_buttons" %>
</div>
<div id="edit_user" class="col-lg-9">
<%= render "shared/settings/setting_view", setting_id: "account", setting_title: t("settings.account.subtitle") %>
</div>
</div>
</div>

View File

@ -18,26 +18,10 @@
<div class="row">
<div class="col-lg-3 mb-4">
<div class="list-group list-group-transparent mb-0">
<button id="users" class="list-group-item list-group-item-action setting-btn <%= "active" if !params[:setting] || params[:setting] == "users"%>">
<span class="icon mr-3"><i class="fas fa-users"></i></span><%= t("administrator.users.title") %>
</button>
<button id="site_settings" class="list-group-item list-group-item-action setting-btn <%= "active" if params[:setting] == "site_settings"%>">
<span class="icon mr-4"><i class="fas fa-cogs"></i></span><%= t("administrator.site_settings.title") %>
</button>
</div>
<%= render "admins/components/menu_buttons" %>
</div>
<div class="col-lg-9">
<% if defined?(setting_id) && setting_id == "account" %>
<%= render "shared/settings/setting_view", setting_id: "account", setting_title: t("administrator.users.edit.title") %>
<% else %>
<%= render "shared/settings/setting_view", admin_view: true, setting_id: "users", setting_title: t("administrator.users.title") %>
<%= render "shared/settings/setting_view", admin_view: true, setting_id: "site_settings", setting_title: t("administrator.site_settings.subtitle") %>
<% end %>
<%= render "shared/modals/delete_account_modal", delete_location: relative_root %>
<div id="users" class="col-lg-9">
<%= render "admins/components/setting_view", setting_id: "users", setting_title: t("administrator.users.title"), search: true %>
</div>
</div>
</div>

View File

@ -0,0 +1,27 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="container pt-6">
<%= render "shared/components/subtitle", subtitle: t("administrator.title"), search: false %>
<div class="row">
<div class="col-lg-3 mb-4">
<%= render "admins/components/menu_buttons" %>
</div>
<div id="server_recordings" class="col-lg-9">
<%= render "admins/components/setting_view", setting_id: "recordings", setting_title: t("administrator.recordings.title"), search: true %>
</div>
</div>
</div>

View File

@ -0,0 +1,27 @@
<%
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div class="container pt-6">
<%= render "shared/components/subtitle", subtitle: t("administrator.title"), search: false %>
<div class="row">
<div class="col-lg-3 mb-4">
<%= render "admins/components/menu_buttons" %>
</div>
<div id="site_settings" class="col-lg-9">
<%= render "admins/components/setting_view", setting_id: "settings", setting_title: t("administrator.site_settings.subtitle"), search: false %>
</div>
</div>
</div>

View File

@ -13,9 +13,21 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/room_event' do %>
<% valid_access_code = @room.access_code.nil? || @room.access_code.empty? || @room.access_code == session[:access_code] %>
<%= render 'shared/room_event', render_recordings: valid_access_code do %>
<% if room_authentication_required %>
<h2><%= t("administrator.site_settings.authentication.user-info") %></h2>
<% elsif !valid_access_code %>
<%= form_for :room, url: login_room_path(@room.uid) do |f| %>
<div class="input-group join-input">
<%= f.text_field :access_code,
required: true,
class: "form-control join-form",
placeholder: t("room.enter_the_access_code"),
value: "" %>
<%= f.submit t("room.login"), class: "btn btn-primary btn-sm col-sm-3 form-control join-form" %>
</div>
<% end %>
<% else %>
<%= form_for room_path(@room), method: :post do |f| %>
<div class="input-group join-input">
@ -28,7 +40,7 @@
placeholder: t("enter_your_name"),
value: "#{@name}",
readonly: !current_user.nil? %>
<%= f.submit t("room.join"), class: "btn btn-primary btn-sm col-sm-3 form-control join-form" %>
<%= f.submit (!@is_running && @anyone_can_start)? t("room.start") : t("room.join"), class: "btn btn-primary btn-sm col-sm-3 form-control join-form" %>
</div>
<% end %>
<% end %>

View File

@ -36,7 +36,7 @@
<% unless exceeds_limit %>
<label class="form-label"><%= t("room.invite_participants") %></label>
<div class="row">
<div class="col-lg-5 col-md-12 mt-2 pr-0">
<div class="col-lg-7 col-md-12 mt-2 pr-0">
<div class="input-icon invite-link-input">
<span class="input-icon-addon">
<i class="fas fa-link"></i>
@ -44,7 +44,7 @@
<input id="invite-url" type="text" class="form-control w-100" value="<%= request.base_url + @room.invite_path %>" readonly="">
</div>
</div>
<div class="col-lg-7 col-md-12 pr-0">
<div class="col-lg-5 col-md-12 pr-0">
<div class="row">
<div class="col-sm-6">
<a href="#" id="copy" class="btn btn-primary btn-block mt-2">
@ -89,7 +89,7 @@
<%= render "shared/components/room_block", room: room %>
<% end %>
</div>
<%= render "shared/modals/delete_room_modal", room: room %>
<%= render "shared/modals/delete_room_modal", recording_count: room.recording_count, room: room %>
<% end %>
<% end %>
<% unless room_limit_exceeded %>

View File

@ -13,7 +13,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<%= render 'shared/room_event' do %>
<%= render 'shared/room_event', render_recordings: true do %>
<div class="row">
<div class="col-9">
<h3><%= t("room.wait.message") %></h3>

View File

@ -23,7 +23,7 @@
<div class="d-flex ml-auto">
<% if current_user %>
<% if current_user.has_role? :super_admin %>
<% if current_user.has_cached_role? :super_admin %>
<% admins_page = params[:controller] == "admins" && params[:action] == "index" ? "active" : "" %>
<%= link_to admins_path, class: "px-3 mx-1 mt-1 header-nav #{admins_page}" do %>
<i class="fas fa-home pr-1 "></i> <%= t("header.dropdown.home") %>
@ -56,7 +56,7 @@
<%= link_to edit_user_path(current_user), class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-id-card mr-3"></i><%= t("header.dropdown.settings") %>
<% end %>
<% if current_user.has_role? :admin %>
<% if current_user.has_cached_role? :admin %>
<%= link_to admins_path, class: "dropdown-item" do %>
<i class="dropdown-icon fas fa-user-tie mr-3"></i><%= t("header.dropdown.account_settings") %>
<% end %>

View File

@ -40,4 +40,6 @@
</div>
</div>
<%= render "shared/sessions", recordings: @public_recordings, pagy: @pagy, only_public: true, user_recordings: false, title: t("room.recordings") %>
<% if render_recordings %>
<%= render "shared/sessions", recordings: @public_recordings, pagy: @pagy, only_public: true, user_recordings: false, title: t("room.recordings") %>
<% end %>

View File

@ -13,7 +13,7 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
%>
<div id="<%= if room == current_user.main_room then 'home_room_block' else 'room-block' end %>" data-room-uid="<%= room.uid %>" data-room-settings=<%= room.room_settings %> class="card">
<div id="<%= if room == current_user.main_room then 'home_room_block' else 'room-block' end %>" data-room-uid="<%= room.uid %>" data-room-settings=<%= room.room_settings %> data-room-access-code="<%= room.access_code %>" class="card">
<div class="card-body p-1">
<table class="table table-hover table-vcenter text-wrap table-no-border">
<tbody class="no-border-top">

View File

@ -28,10 +28,21 @@
<span class="input-icon-addon">
<i class="fas fa-chalkboard-teacher"></i>
</span>
<%= f.text_field :name, id: "create-room-name", class: "form-control", value: "", placeholder: t("modal.create_room.name_placeholder"), autocomplete: :off %>
<%= f.text_field :name, id: "create-room-name", class: "form-control text-center", value: "", placeholder: t("modal.create_room.name_placeholder"), autocomplete: :off %>
<div class="invalid-feedback text-left"><%= t("modal.create_room.not_blank") %></div>
</div>
<div class="input-icon mb-2">
<span onclick="generateAccessCode()" class="input-icon-addon allow-icon-click">
<i class="fas fa-dice"></i>
</span>
<%= f.label :access_code, t("modal.create_room.access_code_placeholder"), id: "create-room-access-code", class: "form-control" %>
<%= f.hidden_field :access_code %>
<span onclick="ResetAccessCode()" class="input-icon-addon allow-icon-click">
<i class="far fa-trash-alt"></i>
</span>
</div>
<% if Rails.configuration.room_features.include? "default-client" %>
<label class="mt-3 mb-3 w-100 text-left d-inline-block">
<input type="hidden" name="room[client]" id="room_client">
@ -57,6 +68,14 @@
</label>
<% end %>
<% if Rails.configuration.room_features.include? "anyone-can-start" %>
<label class="custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block">
<span class="custom-switch-description"><%= t("modal.room_settings.start")%></span>
<%= f.check_box :anyone_can_start, class: "custom-switch-input", checked: false %>
<span class="custom-switch-indicator float-right"></span>
</label>
<% end %>
<label id="auto-join-label" class="create-only custom-switch pl-0 mt-3 mb-3 w-100 text-left d-inline-block">
<span class="custom-switch-description"><%= t("modal.create_room.auto_join") %></span>
<%= f.check_box :auto_join, class: "custom-switch-input", checked: false %>

View File

@ -34,8 +34,8 @@
<div class="card-footer">
<p>
<%= t("modal.delete_room.warning").html_safe %>
<% if room.recordings.length > 0 %>
<%= t("modal.delete_room.recording_warning", recordings_num: room.recordings.length).html_safe %>
<% if recording_count > 0 %>
<%= t("modal.delete_room.recording_warning", recordings_num: recording_count).html_safe %>
<% end %>
</p>
</div>

View File

@ -20,17 +20,17 @@
<div class="col-lg-3 mb-4">
<div class="list-group list-group-transparent mb-0">
<button id="account" class="list-group-item list-group-item-action setting-btn <%= "active" if !params[:setting] || params[:setting] == "account"%>">
<button id="account" class="list-group-item list-group-item-action dropdown-item setting-btn <%= "active" if !params[:setting] || params[:setting] == "account"%>">
<span class="icon mr-3"><i class="fas fa-user"></i></span><%= t("settings.account.title") %>
</button>
<% if @user.social_uid.nil? %>
<button id="password" class="list-group-item list-group-item-action setting-btn <%= "active" if params[:setting] == "password"%>">
<button id="password" class="list-group-item list-group-item-action dropdown-item setting-btn <%= "active" if params[:setting] == "password"%>">
<span class="icon mr-3"><i class="fas fa-lock"></i></span><%= t("settings.password.title") %>
</button>
<% end %>
<button id="delete" class="list-group-item list-group-item-action setting-btn <%= "active" if params[:setting] == "delete"%>">
<button id="delete" class="list-group-item list-group-item-action dropdown-item setting-btn <%= "active" if params[:setting] == "delete"%>">
<span class="icon mr-3"><i class="fas fa-trash-alt"></i></span><%= t("settings.delete.title") %>
</button>
</div>

View File

@ -1,5 +1,5 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
load Gem.bin_path('bundler', 'bundle')

View File

@ -1,12 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
@ -20,6 +19,9 @@ chdir APP_ROOT do
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
# puts "\n== Copying sample files =="
# unless File.exist?('config/database.yml')
# cp 'config/database.yml.sample', 'config/database.yml'

View File

@ -1,12 +1,11 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'pathname'
require 'fileutils'
include FileUtils
# path to your application root.
APP_ROOT = Pathname.new File.expand_path('../../', __FILE__)
APP_ROOT = File.expand_path('..', __dir__)
def system!(*args)
system(*args) || abort("\n== Command #{args} failed ==")
@ -20,6 +19,9 @@ chdir APP_ROOT do
system! 'gem install bundler --conservative'
system('bundle check') || system!('bundle install')
# Install JavaScript dependencies if using Yarn
# system('bin/yarn')
puts "\n== Updating database =="
system! 'bin/rails db:migrate'

13
bin/yarn Executable file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
APP_ROOT = File.expand_path('..', __dir__)
Dir.chdir(APP_ROOT) do
begin
exec "yarnpkg", *ARGV
rescue Errno::ENOENT
warn "Yarn executable was not detected in the system."
warn "Download Yarn at https://yarnpkg.com/en/docs/install"
exit 1
end
end

View File

@ -26,6 +26,9 @@ Bundler.require(*Rails.groups)
module Greenlight
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 5.2
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.

View File

@ -3,3 +3,4 @@
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
require 'bundler/setup' # Set up gems listed in the Gemfile.
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.

View File

@ -15,12 +15,13 @@ Rails.application.configure do
config.consider_all_requests_local = false
# Enable/disable caching. By default caching is disabled.
if Rails.root.join('tmp/caching-dev.txt').exist?
# Run rails dev:cache to toggle caching.
if Rails.root.join('tmp', 'caching-dev.txt').exist?
config.action_controller.perform_caching = true
config.cache_store = :memory_store
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=172800',
'Cache-Control' => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
@ -28,6 +29,9 @@ Rails.application.configure do
config.cache_store = :null_store
end
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# Don't wrap form components in field_with_error divs
ActionView::Base.field_error_proc = proc do |html_tag|
html_tag.html_safe
@ -57,6 +61,9 @@ Rails.application.configure do
# Raise an error on page load if there are pending migrations.
config.active_record.migration_error = :page_load
# Highlight code that triggered database queries in logs.
config.active_record.verbose_query_logs = true
# Debug mode disables concatenation and preprocessing of assets.
# This option may cause significant delays in view rendering with a large
# number of complex assets.

View File

@ -16,6 +16,10 @@ Rails.application.configure do
config.consider_all_requests_local = false
config.action_controller.perform_caching = true
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
# config.require_master_key = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].blank?
@ -36,6 +40,9 @@ Rails.application.configure do
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# Mount Action Cable outside main process or domain
# config.action_cable.mount_path = nil
# config.action_cable.url = 'wss://example.com/cable'

View File

@ -17,7 +17,7 @@ Rails.application.configure do
# Configure public file server for tests with Cache-Control for performance.
config.public_file_server.enabled = true
config.public_file_server.headers = {
'Cache-Control' => 'public, max-age=3600',
'Cache-Control' => "public, max-age=#{1.hour.to_i}"
}
# Show full error reports and disable caching.
@ -29,6 +29,10 @@ Rails.application.configure do
# Disable request forgery protection in test environment.
config.action_controller.allow_forgery_protection = false
# Store uploaded files on the local file system in a temporary directory
config.active_storage.service = :test
config.action_mailer.perform_caching = false
# Tell Action Mailer not to deliver emails to the real world.

View File

@ -5,9 +5,12 @@
# Version of your assets, change this if you want to expire all your assets.
Rails.application.config.assets.version = '1.0'
# Add additional assets to the asset load path
# Add additional assets to the asset load path.
# Rails.application.config.assets.paths << Emoji.images_path
# Add Yarn node_modules folder to the asset load path.
Rails.application.config.assets.paths << Rails.root.join('node_modules')
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
# Rails.application.config.assets.precompile += %w()
# application.js, application.css, and all non-JS/CSS in the app/assets
# folder are already added.
# Rails.application.config.assets.precompile += %w( admin.js admin.css )

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
# Define an application-wide content security policy
# For further information see the following documentation
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
# Rails.application.config.content_security_policy do |policy|
# policy.default_src :self, :https
# policy.font_src :self, :https, :data
# policy.img_src :self, :https, :data
# policy.object_src :none
# policy.script_src :self, :https
# policy.style_src :self, :https
# # Specify URI for violation reports
# # policy.report_uri "/csp-violation-report-endpoint"
# end
# If you are using UJS then enable automatic nonce generation
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
# Report CSP violations to a specified URI
# For further information see the following documentation:
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
# Rails.application.config.content_security_policy_report_only = true

View File

@ -23,17 +23,11 @@ HealthCheck.setup do |config|
config.http_status_for_error_object = 500
# bucket names to test connectivity - required only if s3 check used, access permissions can be mixed
config.buckets = { 'bucket_name' => [:R, :W, :D] }
# You can customize which checks happen on a standard health check, eg to set an explicit list use:
config.standard_checks = %w(database migrations custom)
# Or to exclude one check:
config.standard_checks -= %w(emailconf)
config.standard_checks = %w(database migrations emailconf)
# You can set what tests are run with the 'full' or 'all' parameter
config.full_checks = %w(database migrations custom email cache redis resque-redis sidekiq-redis s3)
config.full_checks = %w(database migrations email cache)
# max-age of response in seconds
# cache-control is public when max_age > 1 and basic_auth_username is not set
@ -43,7 +37,4 @@ HealthCheck.setup do |config|
# http status code used when the ip is not allowed for the request
config.http_status_for_ip_whitelist_error = 403
# When redis url is non-standard
config.redis_url = 'redis_url'
end

View File

@ -1,28 +0,0 @@
# frozen_string_literal: true
# Be sure to restart your server when you modify this file.
#
# This file contains migration options to ease your Rails 5.0 upgrade.
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
Rails.application.config.action_controller.raise_on_unfiltered_parameters = true
# Enable per-form CSRF tokens. Previous versions had false.
Rails.application.config.action_controller.per_form_csrf_tokens = true
# Enable origin-checking CSRF mitigation. Previous versions had false.
Rails.application.config.action_controller.forgery_protection_origin_check = true
# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`.
# Previous versions had false.
ActiveSupport.to_time_preserves_timezone = true
# Require `belongs_to` associations by default. Previous versions had false.
Rails.application.config.active_record.belongs_to_required_by_default = true
# Do not halt callback chains when a callback returns false. Previous versions had true.
ActiveSupport.halt_callback_chains_on_return_false = false
# Configure SSL options to enable HSTS with subdomains. Previous versions had false.
Rails.application.config.ssl_options = { hsts: { subdomains: true } }

View File

@ -2,4 +2,9 @@
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_greenlight-2_0_session'
if Rails.configuration.loadbalanced_configuration
Rails.application.config.session_store :cookie_store, key: '_greenlight-2_0_session',
domain: ENV['GREENLIGHT_PARENT_DOMAIN'] || 'blindside-dev.com'
else
Rails.application.config.session_store :cookie_store, key: '_greenlight-2_0_session'
end

View File

@ -48,6 +48,12 @@ en:
regular: Regular
lighten: Lighten
darken: Darken
recording_visibility:
public: Public
private: Private
info: Set the default recording visbility for new recordings
title: Recording Default Visibility
warning: This setting will only be applied to rooms that aren't running
registration:
info: Change the way that users register to the website
title: Registration Method
@ -73,6 +79,9 @@ en:
registration_method_updated: Registration method successfully updated
settings: Site Settings successfully changed
unauthorized: You are not authorized to perform actions on this user
recordings:
title: Server Recordings
no_recordings: This server has no recordings.
title: Organization Settings
users:
invite: Invite User
@ -256,6 +265,8 @@ en:
max_concurrent: The maximum number of concurrent sessions allowed has been reached!
modal:
create_room:
access_code: Access Code
access_code_placeholder: Generate an optional room access code
auto_join: Automatically join me into the room
create: Create Room
free_delete: You will be free to delete this room at any time.
@ -288,6 +299,7 @@ en:
update: Update Room
client: Select client type
mute: Mute users when they join
start: Allow any user to start this meeting
default: Default
html: HTML5
flash: Flash
@ -359,19 +371,22 @@ en:
confirm: New Password Confirmation
update: Update Password
roles:
administrator: Administrator
administrator: Admin
banned: Banned
pending: Pending
super_admin: Super Admin
user: User
room:
access_code_required: Please enter a valid access code to join the room
create_room: Create a Room
create_room_error: There was an error creating the room
create_room_success: Room created successfully
enter_the_access_code: Enter the room's access code
invited: You have been invited to join
invite_participants: Invite Participants
join: Join
last_session: Last session on %{session}
login: Login
owner: Owner
no_sessions: This room has no sessions, yet!
recordings: Room Recordings

View File

@ -1,15 +1,15 @@
# frozen_string_literal: true
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers a minimum and maximum.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum, this matches the default thread size of Active Record.
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count
# Specifies the `port` that Puma will listen on to receive requests, default is 3000.
# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port ENV.fetch("PORT") { 80 }
@ -28,22 +28,9 @@ environment ENV.fetch("RAILS_ENV") { "development" }
# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory. If you use this option
# you need to make sure to reconnect any threads in the `on_worker_boot`
# block.
# process behavior so workers use less memory.
#
# preload_app!
# The code in the `on_worker_boot` will be called if you are using
# clustered mode by specifying a number of `workers`. After each worker
# process is booted this block will be run, if you are using `preload_app!`
# option you will want to use this block to reconnect to any threads
# or connections that may have been created at application boot, Ruby
# cannot share connections between processes.
#
# on_worker_boot do
# ActiveRecord::Base.establish_connection if defined?(ActiveRecord)
# end
# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

View File

@ -37,6 +37,8 @@ Rails.application.routes.draw do
resources :admins, only: [:index]
scope '/admins' do
get '/site_settings', to: 'admins#site_settings', as: :admin_site_settings
get '/recordings', to: 'admins#server_recordings', as: :admin_recordings
post '/branding', to: 'admins#branding', as: :admin_branding
post '/coloring', to: 'admins#coloring', as: :admin_coloring
post '/room_authentication', to: 'admins#room_authentication', as: :admin_room_authentication
@ -52,6 +54,7 @@ Rails.application.routes.draw do
post '/registration_method/:method', to: 'admins#registration_method', as: :admin_change_registration
post '/approve/:user_uid', to: 'admins#approve', as: :admin_approve
post '/room_limit', to: 'admins#room_limit', as: :admin_room_limit
post '/default_recording_visibility', to: 'admins#default_recording_visibility', as: :admin_recording_visibility
end
scope '/themes' do
@ -100,6 +103,7 @@ Rails.application.routes.draw do
post '/update_settings', to: 'rooms#update_settings'
post '/start', to: 'rooms#start', as: :start_room
get '/logout', to: 'rooms#logout', as: :logout_room
post '/login', to: 'rooms#login', as: :login_room
end
# Recording operations routes

View File

@ -1,8 +1,8 @@
# frozen_string_literal: true
%w(
%w[
.ruby-version
.rbenv-vars
tmp/restart.txt
tmp/caching-dev.txt
).each { |path| Spring.watch(path) }
].each { |path| Spring.watch(path) }

34
config/storage.yml Normal file
View File

@ -0,0 +1,34 @@
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# amazon:
# service: S3
# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
# region: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
# mirror:
# service: Mirror
# primary: local
# mirrors: [ amazon, google, microsoft ]

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddPasswordDigestToRooms < ActiveRecord::Migration[5.0]
def change
add_column :rooms, :access_code, :string, null: true, default: nil
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190522195242) do
ActiveRecord::Schema.define(version: 20190711192033) do
create_table "features", force: :cascade do |t|
t.integer "setting_id"
@ -56,6 +56,7 @@ ActiveRecord::Schema.define(version: 20190522195242) do
t.string "room_settings", default: "{ }"
t.string "moderator_pw"
t.string "attendee_pw"
t.string "access_code"
t.index ["bbb_id"], name: "index_rooms_on_bbb_id"
t.index ["last_session"], name: "index_rooms_on_last_session"
t.index ["name"], name: "index_rooms_on_name"

View File

@ -12,15 +12,9 @@ module BbbApi
end
# Sets a BigBlueButtonApi object for interacting with the API.
def bbb
def bbb(user_provider)
if Rails.configuration.loadbalanced_configuration
if instance_of? Room
# currently in the Room Model
user_domain = retrieve_provider_info(owner.provider)
elsif instance_of? User
# currently in the User Model
user_domain = retrieve_provider_info(provider)
end
user_domain = retrieve_provider_info(user_provider)
BigBlueButton::BigBlueButtonApi.new(remove_slash(user_domain["apiURL"]), user_domain["secret"], "0.8")
else

View File

@ -1,7 +1,7 @@
# Create a Secret Key for Rails
#
# You can generate a secure one through the Greenlight docker image
# with with the command.
# with the command.
#
# docker run --rm bigbluebutton/greenlight:v2 bundle exec rake secret
#
@ -20,7 +20,7 @@ BIGBLUEBUTTON_SECRET=
#
# For in-depth steps on setting up a Google Login Provider, see:
#
# http://docs.bigbluebutton.org/install/greenlight-v2.html#google-oauth2
# https://docs.bigbluebutton.org/greenlight/gl-customize.html#google-oauth2
#
# The GOOGLE_OAUTH2_HD variable is used to limit sign-ins to a particular set of Google Apps hosted
# domains. This can be a string with separating commas such as, 'domain.com, example.com' or
@ -32,18 +32,13 @@ GOOGLE_OAUTH2_HD=
# Twitter Login Provider (optional)
#
# For in-depth steps on setting up a Twitter Login Provider, see:
#
# http://docs.bigbluebutton.org/install/greenlight-v2.html#twitter-oauth2
#
TWITTER_ID=
TWITTER_SECRET=
# Twitter Authentication is deprecated and will be phased out in a future release.
# Microsoft Office365 Login Provider (optional)
#
# For in-depth steps on setting up a Office 365 Login Provider, see:
#
# http://docs.bigbluebutton.org/install/greenlight-v2.html#office365-oauth2
# https://docs.bigbluebutton.org/greenlight/gl-customize.html#office365-oauth2
#
OFFICE365_KEY=
OFFICE365_SECRET=
@ -55,7 +50,7 @@ OFFICE365_HD=
# Configuring LDAP authentication will take precedence over all other providers.
# For information about setting up LDAP, see:
#
# http://docs.bigbluebutton.org/install/greenlight-v2.html#ldap-auth
# https://docs.bigbluebutton.org/greenlight/gl-customize.html#ldap-auth
#
# LDAP_SERVER=ldap.example.com
# LDAP_PORT=389
@ -75,7 +70,7 @@ LDAP_PASSWORD=
# Set this to true if you want GreenLight to support user signup and login without
# Omniauth. For more information, see:
#
# http://docs.bigbluebutton.org/install/greenlight-v2.html#in-application-greenlight
# https://docs.bigbluebutton.org/greenlight/gl-overview.html#accounts-and-profile
#
ALLOW_GREENLIGHT_ACCOUNTS=true
@ -130,7 +125,8 @@ RELATIVE_URL_ROOT=/b
# Current settings available:
# default-client: Room owners can decide between the Flash Client and the HTML5 Client for a room
# mute-on-join: Automatically mute users by default when they join a room
ROOM_FEATURES=default-client,mute-on-join
# anyone-can-start: Allows anyone with the join url to start the room in BigBlueButton
ROOM_FEATURES=default-client,mute-on-join,anyone-can-start
# Specify the maximum number of records to be sent to the BigBlueButton API in one call
# Default is set to 25 records

View File

@ -0,0 +1,567 @@
# frozen_string_literal: true
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
#
# Copyright (c) 2018 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.
#
# BigBlueButton 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 BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
require "rails_helper"
require 'bigbluebutton_api'
shared_examples_for "recorder" do
let(:controller) { described_class } # the class that includes the concern
before do
@user = create(:user)
@room = @user.main_room
allow_any_instance_of(Room).to receive(:owner).and_return(@user)
end
it "should properly find meeting recordings" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
name: "Example",
playback: {
format:
{
type: "presentation"
}
}
}
]
)
expect(recordings(@room.bbb_id, @room.owner.provider)).to contain_exactly(
name: "Example",
playbacks:
[
{
type: "presentation"
}
]
)
end
it "gets all filtered and sorted recordings for the user" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "5",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
}
},
{
meetingID: @room.bbb_id,
name: "test",
participants: "1",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "Exam",
participants: "1",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
name: "z",
}
}
]
)
expect(all_recordings(@user.rooms.pluck(:bbb_id), @user.provider, search: "Exam", column: "name",
direction: "desc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
}
]
)
end
context '#filtering' do
before do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "5",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
}
},
{
meetingID: @room.bbb_id,
name: "test",
participants: "1",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "Exam",
participants: "1",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
name: "metadata",
}
}
]
)
end
it "should filter recordings on name" do
expect(recordings(@room.bbb_id, @room.owner.provider, search: "Exam")).to contain_exactly(
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
},
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on participants" do
expect(recordings(@room.bbb_id, @room.owner.provider, search: "5")).to contain_exactly(
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
)
end
it "should filter recordings on format" do
expect(recordings(@room.bbb_id, @room.owner.provider, search: "presentation")).to contain_exactly(
{
meetingID: @room.bbb_id,
name: "test",
participants: "1",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
},
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on visibility" do
expect(recordings(@room.bbb_id, @room.owner.provider, search: "public")).to contain_exactly(
{
meetingID: @room.bbb_id,
name: "test",
participants: "1",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
},
},
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on metadata name by default" do
expect(recordings(@room.bbb_id, @room.owner.provider, search: "metadata")).to contain_exactly(
meetingID: @room.bbb_id,
name: "Exam",
participants: "1",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
name: "metadata",
}
)
end
end
context '#sorting' do
before do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playback: {
format: {
type: "presentation",
length: "4"
}
},
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playback: {
format: {
type: "other",
length: "3"
}
},
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on name" do
expect(recordings(@room.bbb_id, @room.owner.provider, column: "name", direction: "asc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on participants" do
expect(recordings(@room.bbb_id, @room.owner.provider, column: "users", direction: "desc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on visibility" do
expect(recordings(@room.bbb_id, @room.owner.provider, column: "visibility", direction: "desc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on length" do
expect(recordings(@room.bbb_id, @room.owner.provider, column: "length", direction: "asc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
},
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
}
]
)
end
it "should sort recordings on format" do
expect(recordings(@room.bbb_id, @room.owner.provider, column: "formats", direction: "desc")).to eq(
[
{
meetingID: @room.bbb_id,
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
meetingID: @room.bbb_id,
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
end
end

View File

@ -20,6 +20,8 @@ require "rails_helper"
describe AdminsController, type: :controller do
before do
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
controller.instance_variable_set(:@user_domain, "provider1")
@user = create(:user, provider: "provider1")
@admin = create(:user, provider: "provider1")
@admin.add_role :admin
@ -52,7 +54,7 @@ describe AdminsController, type: :controller do
get :edit_user, params: { user_uid: @user.uid }
expect(response).to render_template(:index)
expect(response).to render_template(:edit_user)
end
end
@ -144,7 +146,7 @@ describe AdminsController, type: :controller do
email = Faker::Internet.email
post :invite, params: { invite_user: { email: email } }
invite = Invitation.find_by(email: email, provider: "greenlight")
invite = Invitation.find_by(email: email, provider: "provider1")
expect(invite.present?).to eq(true)
expect(flash[:success]).to be_present
@ -197,7 +199,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Branding Image")
expect(feature[:value]).to eq(fake_image_url)
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
end
@ -214,7 +216,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color")
expect(feature[:value]).to eq(primary_color)
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
it "changes the primary-lighten on the page" do
@ -229,7 +231,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Lighten")
expect(feature[:value]).to eq(primary_color)
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
it "changes the primary-darken on the page" do
@ -244,7 +246,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Darken")
expect(feature[:value]).to eq(primary_color)
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
end
end
@ -264,7 +266,7 @@ describe AdminsController, type: :controller do
expect(feature[:value]).to eq(Rails.configuration.registration_methods[:invite])
expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
it "does not allow the user to change to invite if emails are off" do
@ -277,7 +279,7 @@ describe AdminsController, type: :controller do
post :registration_method, params: { method: "invite" }
expect(flash[:alert]).to be_present
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
end
@ -293,7 +295,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Authentication")
expect(feature[:value]).to eq("true")
expect(response).to redirect_to(admins_path)
expect(response).to redirect_to(admin_site_settings_path)
end
end
@ -309,6 +311,22 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Limit")
expect(feature[:value]).to eq("5")
expect(response).to redirect_to(admin_site_settings_path)
end
end
context "POST #default_recording_visibility" do
it "changes the default recording visibility setting" do
allow(Rails.configuration).to receive(:loadbalanced_configuration).and_return(true)
allow_any_instance_of(User).to receive(:greenlight_account?).and_return(true)
@request.session[:user_id] = @admin.id
post :default_recording_visibility, params: { visibility: "public" }
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Default Recording Visibility")
expect(feature[:value]).to eq("public")
expect(response).to redirect_to(admins_path)
end
end

View File

@ -28,6 +28,8 @@ def random_valid_room_params
end
describe RoomsController, type: :controller do
it_behaves_like "recorder"
include Recorder
describe "GET #show" do
before do
@user = create(:user)
@ -39,7 +41,7 @@ describe RoomsController, type: :controller do
get :show, params: { room_uid: @owner.main_room }
expect(assigns(:recordings)).to eql(@owner.main_room.recordings)
expect(assigns(:recordings)).to eql(recordings(@owner.main_room.bbb_id, @owner.provider))
expect(assigns(:is_running)).to eql(@owner.main_room.running?)
end
@ -117,8 +119,8 @@ describe RoomsController, type: :controller do
@request.session[:user_id] = @owner.id
name = Faker::Games::Pokemon.name
room_params = { name: name, "client": "html5", "mute_on_join": "1" }
json_room_settings = "{\"muteOnStart\":true,\"joinViaHtml5\":true}"
room_params = { name: name, "client": "html5", "mute_on_join": "1", "anyone_can_start": "1" }
json_room_settings = "{\"muteOnStart\":true,\"joinViaHtml5\":true,\"anyoneCanStart\":true}"
post :create, params: { room: room_params }
@ -201,6 +203,46 @@ describe RoomsController, type: :controller do
expect(response).to render_template(:wait)
end
it "should join the room if the room has the anyone_can_start setting" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false)
room = Room.new(name: "test")
room.room_settings = "{\"muteOnStart\":false,\"joinViaHtml5\":false,\"anyoneCanStart\":true}"
room.owner = @owner
room.save
@request.session[:user_id] = @user.id
post :join, params: { room_uid: room, join_name: @user.name }
expect(response).to redirect_to(room.join_path(@user.name, { user_is_moderator: true }, @user.uid))
end
it "should render wait if the correct access code is supplied" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false)
protected_room = Room.new(name: 'test', access_code: "123456")
protected_room.owner = @owner
protected_room.save
@request.session[:user_id] = @user.id
post :join, params: { room_uid: protected_room, join_name: @user.name }, session: { access_code: "123456" }
expect(response).to render_template(:wait)
end
it "should redirect to login if the correct access code isn't supplied" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(false)
protected_room = Room.new(name: 'test', access_code: "123456")
protected_room.owner = @owner
protected_room.save
@request.session[:user_id] = @user.id
post :join, params: { room_uid: protected_room, join_name: @user.name }, session: { access_code: "123455" }
expect(response).to redirect_to room_path(protected_room.uid)
end
it "should join owner as moderator if meeting running" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:is_meeting_running?).and_return(true)
@ -315,7 +357,8 @@ describe RoomsController, type: :controller do
@request.session[:user_id] = @user.id
room_params = { "client": "html5", "mute_on_join": "1", "name": @secondary_room.name }
formatted_room_params = "{\"muteOnStart\":true,\"joinViaHtml5\":true}" # JSON string format
formatted_room_params = "{\"muteOnStart\":true,\"joinViaHtml5\":true,\"anyoneCanStart\":false}"
# JSON string format
expect { post :update_settings, params: { room_uid: @secondary_room.uid, room: room_params } }
.to change { @secondary_room.reload.room_settings }
@ -372,4 +415,27 @@ describe RoomsController, type: :controller do
expect(response).to redirect_to(@room)
end
end
describe "POST #login" do
before do
@user = create(:user)
@room = @user.main_room
@room.access_code = "123456"
@room.save
end
it "should redirect to show with valid access code" do
post :login, params: { room_uid: @room.uid, room: { access_code: "123456" } }
expect(response).to redirect_to room_path(@room.uid)
expect(flash[:alert]).to be_nil
end
it "should redirect to show with and notify user of invalid access code" do
post :login, params: { room_uid: @room.uid, room: { access_code: "123455" } }
expect(response).to redirect_to room_path(@room.uid)
expect(flash[:alert]).to eq(I18n.t("room.access_code_required"))
end
end
end

View File

@ -42,9 +42,13 @@ describe ThemesController, type: :controller do
it "returns the correct color based on provider" do
allow(Rails.configuration).to receive(:loadbalanced_configuration).and_return(true)
allow_any_instance_of(User).to receive(:greenlight_account?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
color1 = Faker::Color.hex_color
provider1 = Faker::Company.name
controller.instance_variable_set(:@user_domain, provider1)
Setting.create(provider: provider1).features.create(name: "Primary Color", value: color1, enabled: true)
user1 = create(:user, provider: provider1)

View File

@ -87,6 +87,8 @@ describe UsersController, type: :controller do
it "allows admins to edit other users" do
allow(Rails.configuration).to receive(:loadbalanced_configuration).and_return(true)
allow_any_instance_of(User).to receive(:greenlight_account?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
controller.instance_variable_set(:@user_domain, "provider1")
user = create(:user, provider: "provider1")
user.add_role :admin
@ -302,6 +304,7 @@ describe UsersController, type: :controller do
describe "PATCH #update" do
it "properly updates user attributes" do
user = create(:user)
@request.session[:user_id] = user.id
params = random_valid_user_params
patch :update, params: params.merge!(user_uid: user)
@ -315,6 +318,7 @@ describe UsersController, type: :controller do
it "renders #edit on unsuccessful save" do
@user = create(:user)
@request.session[:user_id] = @user.id
patch :update, params: invalid_params.merge!(user_uid: @user)
expect(response).to render_template(:edit)
@ -337,6 +341,8 @@ describe UsersController, type: :controller do
allow(Rails.configuration).to receive(:loadbalanced_configuration).and_return(true)
allow_any_instance_of(User).to receive(:greenlight_account?).and_return(true)
allow_any_instance_of(Room).to receive(:delete_all_recordings).and_return('')
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider1")
controller.instance_variable_set(:@user_domain, "provider1")
user = create(:user, provider: "provider1")
admin = create(:user, provider: "provider1")
@ -352,6 +358,8 @@ describe UsersController, type: :controller do
it "doesn't allow admins of other providers to delete users" do
allow(Rails.configuration).to receive(:loadbalanced_configuration).and_return(true)
allow_any_instance_of(User).to receive(:greenlight_account?).and_return(true)
allow_any_instance_of(ApplicationController).to receive(:set_user_domain).and_return("provider2")
controller.instance_variable_set(:@user_domain, "provider2")
user = create(:user, provider: "provider1")
admin = create(:user, provider: "provider2")

View File

@ -133,420 +133,6 @@ describe Room, type: :model do
end
context "#recordings" do
it "should properly find meeting recordings" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
name: "Example",
playback: {
format:
{
type: "presentation"
}
}
}
]
)
expect(@room.recordings).to contain_exactly(
name: "Example",
playbacks:
[
{
type: "presentation"
}
]
)
end
context '#filtering' do
before do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
name: "Example",
participants: "3",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "5",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
}
},
{
name: "test",
participants: "1",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
name: "Exam",
participants: "1",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
name: "z",
}
}
]
)
end
it "should filter recordings on name" do
expect(@room.recordings(search: "Exam")).to contain_exactly(
{
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
},
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on participants" do
expect(@room.recordings(search: "5")).to contain_exactly(
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
)
end
it "should filter recordings on format" do
expect(@room.recordings(search: "presentation")).to contain_exactly(
{
name: "test",
participants: "1",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
},
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on visibility" do
expect(@room.recordings(search: "public")).to contain_exactly(
{
name: "test",
participants: "1",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
},
},
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
)
end
it "should filter recordings on metadata name by default" do
expect(@room.recordings(search: "z")).to contain_exactly(
name: "Exam",
participants: "1",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
name: "z",
}
)
end
end
context '#sorting' do
before do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
name: "Example",
participants: "3",
playback: {
format: {
type: "presentation",
length: "4"
}
},
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "1",
playback: {
format: {
type: "other",
length: "3"
}
},
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on name" do
expect(@room.recordings(column: "name", direction: "asc")).to eq(
[
{
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on participants" do
expect(@room.recordings(column: "users", direction: "desc")).to eq(
[
{
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on visibility" do
expect(@room.recordings(column: "visibility", direction: "desc")).to eq(
[
{
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
it "should sort recordings on length" do
expect(@room.recordings(column: "length", direction: "asc")).to eq(
[
{
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
},
{
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
}
]
)
end
it "should sort recordings on format" do
expect(@room.recordings(column: "formats", direction: "desc")).to eq(
[
{
name: "Example",
participants: "3",
playbacks: [
{
type: "presentation",
length: "4"
}
],
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "1",
playbacks: [
{
type: "other",
length: "3"
}
],
metadata: {
name: "Z",
"gl-listed": "false"
}
}
]
)
end
end
it "deletes the recording" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:delete_recordings).and_return(
returncode: true, deleted: true

View File

@ -173,97 +173,4 @@ describe User, type: :model do
.to raise_exception(ActiveRecord::RecordInvalid, "Validation failed: Email can't be blank")
end
end
context '#recordings' do
it "gets all filtered and sorted recordings for the user" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:get_recordings).and_return(
recordings: [
{
name: "Example",
participants: "3",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "5",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
}
},
{
name: "test",
participants: "1",
playback: {
format:
{
type: "presentation"
}
},
metadata: {
"gl-listed": "true",
}
},
{
name: "Exam",
participants: "1",
playback: {
format:
{
type: "other"
}
},
metadata: {
"gl-listed": "false",
name: "z",
}
}
]
)
expect(@user.all_recordings(search: "Exam", column: "name", direction: "desc")).to eq(
[
{
name: "Example",
participants: "3",
playbacks:
[
{
type: "presentation"
}
],
metadata: {
"gl-listed": "true",
}
},
{
name: "aExamaaa",
participants: "5",
playbacks:
[
{
type: "other"
}
],
metadata: {
"gl-listed": "false",
}
}
]
)
end
end
end