GRN2-129: Added server recordings and refactored adminsitrator panel (#662)

* Added server recordings and refactored adminsitrator panel

* Fixed some issues

* Fixed issue with owner email search

* Fixed issue with edit user
This commit is contained in:
farhatahmad 2019-07-22 12:46:48 -04:00 committed by Jesus Federico
parent 8c63f793a5
commit a055b88eb7
38 changed files with 1088 additions and 640 deletions

View File

@ -43,9 +43,9 @@ $(document).on('turbolinks:load', function(){
window.location.replace(url); window.location.replace(url);
}) })
}
/* COLOR SELECTORS */
if (controller == "admins" && action == "site_settings") {
loadColourSelectors() 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 controller = $("body").data('controller');
var action = $("body").data('action'); 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 // Set a room header rename event
var configure_room_header = function(room_title){ var configure_room_header = function(room_title){

View File

@ -39,21 +39,6 @@ $(document).on('turbolinks:load', function(){
}, 2000) }, 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 // Display and update all fields related to creating a room in the createRoomModal

View File

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

View File

@ -20,7 +20,7 @@ $(document).on('turbolinks:load', function(){
var action = $("body").data('action'); var action = $("body").data('action');
// Only run on the settings page. // 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 settingsButtons = $('.setting-btn');
var settingsViews = $('.setting-view'); var settingsViews = $('.setting-view');

View File

@ -18,7 +18,10 @@ $(document).on('turbolinks:load', function(){
var controller = $("body").data('controller'); var controller = $("body").data('controller');
var action = $("body").data('action'); 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 // Choose active header
// (Name, Length or Users) // (Name, Length or Users)

View File

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

View File

@ -20,6 +20,7 @@ class AdminsController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Themer include Themer
include Emailer include Emailer
include Recorder
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve] manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve]
site_settings = [:branding, :coloring, :coloring_lighten, :coloring_darken, site_settings = [:branding, :coloring, :coloring_lighten, :coloring_darken,
@ -40,11 +41,27 @@ class AdminsController < ApplicationController
@pagy, @users = pagy(user_list) @pagy, @users = pagy(user_list)
end 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 # MANAGE USERS
# GET /admins/edit/:user_uid # GET /admins/edit/:user_uid
def edit_user def edit_user
render "admins/index", locals: { setting_id: "account" }
end end
# POST /admins/promote/:user_uid # POST /admins/promote/:user_uid
@ -111,7 +128,7 @@ class AdminsController < ApplicationController
# POST /admins/branding # POST /admins/branding
def branding def branding
@settings.update_value("Branding Image", params[:url]) @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 end
# POST /admins/color # POST /admins/color
@ -119,23 +136,23 @@ class AdminsController < ApplicationController
@settings.update_value("Primary Color", params[:color]) @settings.update_value("Primary Color", params[:color])
@settings.update_value("Primary Color Lighten", color_lighten(params[:color])) @settings.update_value("Primary Color Lighten", color_lighten(params[:color]))
@settings.update_value("Primary Color Darken", color_darken(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 end
def coloring_lighten def coloring_lighten
@settings.update_value("Primary Color Lighten", params[:color]) @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 end
def coloring_darken def coloring_darken
@settings.update_value("Primary Color Darken", params[:color]) @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 end
# POST /admins/room_authentication # POST /admins/room_authentication
def room_authentication def room_authentication
@settings.update_value("Room Authentication", params[:value]) @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 end
# POST /admins/registration_method/:method # POST /admins/registration_method/:method
@ -144,11 +161,11 @@ class AdminsController < ApplicationController
# Only allow change to Join by Invitation if user has emails enabled # 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] 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") } flash: { alert: I18n.t("administrator.flash.invite_email_verification") }
else else
@settings.update_value("Registration Method", new_method) @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") } flash: { success: I18n.t("administrator.flash.registration_method_updated") }
end end
end end
@ -156,7 +173,7 @@ class AdminsController < ApplicationController
# POST /admins/room_limit # POST /admins/room_limit
def room_limit def room_limit
@settings.update_value("Room Limit", params[: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 end
private private

View File

@ -16,11 +16,50 @@
# You should have received a copy of the GNU Lesser General Public License along # You should have received a copy of the GNU Lesser General Public License along
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
module APIConcern module Recorder
extend ActiveSupport::Concern 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 # 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] || "" search = search_params[:search] || ""
order_col = search_params[:column] && search_params[:direction] != "none" ? search_params[:column] : "end_time" 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] : "desc" order_dir = search_params[:column] && search_params[:direction] != "none" ? search_params[:direction] : "desc"
@ -40,7 +79,7 @@ module APIConcern
r.delete(:playback) r.delete(:playback)
end end
recs = filter_recordings(api_res, search) recs = filter_recordings(api_res, search, search_name)
recs = sort_recordings(recs, order_col, order_dir) recs = sort_recordings(recs, order_col, order_dir)
if ret_search_params if ret_search_params
@ -50,7 +89,7 @@ module APIConcern
end end
end end
def filter_recordings(api_res, search) def filter_recordings(api_res, search, search_name = false)
api_res[:recordings].select do |r| api_res[:recordings].select do |r|
(!r[:metadata].nil? && ((!r[:metadata][:name].nil? && (!r[:metadata].nil? && ((!r[:metadata][:name].nil? &&
r[:metadata][:name].downcase.include?(search)) || r[:metadata][:name].downcase.include?(search)) ||
@ -59,7 +98,8 @@ module APIConcern
((r[:metadata].nil? || r[:metadata][:name].nil?) && ((r[:metadata].nil? || r[:metadata][:name].nil?) &&
r[:name].downcase.include?(search)) || r[:name].downcase.include?(search)) ||
r[:participants].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
end end

View File

@ -50,6 +50,11 @@ class RecordingsController < ApplicationController
# Ensure the user is logged into the room they are accessing. # Ensure the user is logged into the room they are accessing.
def verify_room_ownership 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
end end

View File

@ -19,6 +19,7 @@
class RoomsController < ApplicationController class RoomsController < ApplicationController
include RecordingsHelper include RecordingsHelper
include Pagy::Backend include Pagy::Backend
include Recorder
before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms } before_action :validate_accepted_terms, unless: -> { !Rails.configuration.terms }
before_action :validate_verified_email, except: [:show, :join], before_action :validate_verified_email, except: [:show, :join],
@ -56,7 +57,7 @@ class RoomsController < ApplicationController
def show def show
if current_user && @room.owned_by?(current_user) if current_user && @room.owned_by?(current_user)
@search, @order_column, @order_direction, recs = @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) @pagy, @recordings = pagy_array(recs)
@ -72,7 +73,7 @@ class RoomsController < ApplicationController
end end
@search, @order_column, @order_direction, pub_recs = @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) @pagy, @public_recordings = pagy_array(pub_recs)
@ -135,7 +136,7 @@ class RoomsController < ApplicationController
search_params = params[@room.invite_path] || params search_params = params[@room.invite_path] || params
@search, @order_column, @order_direction, pub_recs = @search, @order_column, @order_direction, pub_recs =
@room.public_recordings(search_params.permit(:search, :column, :direction), true) public_recordings(@room.bbb_id, @user_domain, search_params.permit(:search, :column, :direction), true)
@pagy, @public_recordings = pagy_array(pub_recs) @pagy, @public_recordings = pagy_array(pub_recs)

View File

@ -21,6 +21,7 @@ class UsersController < ApplicationController
include Pagy::Backend include Pagy::Backend
include Emailer include Emailer
include Registrar include Registrar
include Recorder
before_action :find_user, only: [:edit, :update, :destroy] before_action :find_user, only: [:edit, :update, :destroy]
before_action :ensure_unauthenticated, only: [:new, :create] before_action :ensure_unauthenticated, only: [:new, :create]
@ -103,6 +104,8 @@ class UsersController < ApplicationController
# PATCH /u/:user_uid/edit # PATCH /u/:user_uid/edit
def update def update
redirect_path = current_user.admin_of?(@user) ? admins_path : edit_user_path(@user)
if params[:setting] == "password" if params[:setting] == "password"
# Update the users password. # Update the users password.
errors = {} errors = {}
@ -123,7 +126,7 @@ class UsersController < ApplicationController
if errors.empty? && @user.save if errors.empty? && @user.save
# Notify the user that their account has been updated. # Notify the user that their account has been updated.
flash[:success] = I18n.t("info_update_success") flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user) redirect_to redirect_path
else else
# Append custom errors. # Append custom errors.
errors.each { |k, v| @user.errors.add(k, v) } errors.each { |k, v| @user.errors.add(k, v) }
@ -132,11 +135,11 @@ class UsersController < ApplicationController
elsif user_params[:email] != @user.email && @user.update_attributes(user_params) elsif user_params[:email] != @user.email && @user.update_attributes(user_params)
@user.update_attributes(email_verified: false) @user.update_attributes(email_verified: false)
flash[:success] = I18n.t("info_update_success") flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user) redirect_to redirect_path
elsif @user.update_attributes(user_params) elsif @user.update_attributes(user_params)
update_locale(@user) update_locale(@user)
flash[:success] = I18n.t("info_update_success") flash[:success] = I18n.t("info_update_success")
redirect_to edit_user_path(@user) redirect_to redirect_path
else else
render :edit, params: { settings: params[:settings] } render :edit, params: { settings: params[:settings] }
end end
@ -165,7 +168,8 @@ class UsersController < ApplicationController
def recordings def recordings
if current_user && current_user.uid == params[:user_uid] if current_user && current_user.uid == params[:user_uid]
@search, @order_column, @order_direction, recs = @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) @pagy, @recordings = pagy_array(recs)
else else
redirect_to root_path redirect_to root_path

View File

@ -19,6 +19,18 @@
module AdminsHelper module AdminsHelper
include Pagy::Frontend 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 def display_invite
current_page?(admins_path) && invite_registration current_page?(admins_path) && invite_registration
end end

View File

@ -19,7 +19,6 @@
require 'bbb_api' require 'bbb_api'
class Room < ApplicationRecord class Room < ApplicationRecord
include ::APIConcern
include ::BbbApi include ::BbbApi
before_create :setup before_create :setup
@ -40,7 +39,7 @@ class Room < ApplicationRecord
# Checks if a room is running on the BigBlueButton server. # Checks if a room is running on the BigBlueButton server.
def running? def running?
bbb.is_meeting_running?(bbb_id) bbb(owner.provider).is_meeting_running?(bbb_id)
end end
# Determines the invite path for the room. # Determines the invite path for the room.
@ -62,7 +61,7 @@ class Room < ApplicationRecord
# Send the create request. # Send the create request.
begin begin
meeting = bbb.create_meeting(name, bbb_id, create_options) meeting = bbb(owner.provider).create_meeting(name, bbb_id, create_options)
# Update session info. # Update session info.
unless meeting[:messageKey] == 'duplicateWarning' unless meeting[:messageKey] == 'duplicateWarning'
update_attributes(sessions: sessions + 1, last_session: DateTime.now) update_attributes(sessions: sessions + 1, last_session: DateTime.now)
@ -84,10 +83,10 @@ class Room < ApplicationRecord
options[:user_is_moderator] ||= false options[:user_is_moderator] ||= false
options[:meeting_recorded] ||= false options[:meeting_recorded] ||= false
return call_invalid_res unless bbb return call_invalid_res unless bbb(owner.provider)
# Get the meeting info. # 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. # Determine the password to use when joining.
password = if options[:user_is_moderator] password = if options[:user_is_moderator]
@ -101,7 +100,7 @@ class Room < ApplicationRecord
join_opts[:userID] = uid if uid join_opts[:userID] = uid if uid
join_opts[:joinViaHtml5] = options[:join_via_html5] if options[:join_via_html5] 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 end
# Notify waiting users that a meeting has started. # Notify waiting users that a meeting has started.
@ -111,7 +110,7 @@ class Room < ApplicationRecord
# Retrieves all the users in a room. # Retrieves all the users in a room.
def participants 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| res[:attendees].map do |att|
User.find_by(uid: att[:userID], name: att[:fullName]) User.find_by(uid: att[:userID], name: att[:fullName])
end end
@ -120,27 +119,18 @@ class Room < ApplicationRecord
[] []
end end
# Fetches all recordings for a room. def recording_count
def recordings(search_params = {}, ret_search_params = false) bbb(owner.provider).get_recordings(meetingID: bbb_id)[:recordings].length
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" }]
end end
def update_recording(record_id, meta) def update_recording(record_id, meta)
meta[:recordID] = record_id meta[:recordID] = record_id
bbb.send_api_request("updateRecordings", meta) bbb(owner.provider).send_api_request("updateRecordings", meta)
end end
# Deletes a recording from a room. # Deletes a recording from a room.
def delete_recording(record_id) def delete_recording(record_id)
bbb.delete_recordings(record_id) bbb(owner.provider).delete_recordings(record_id)
end end
private private
@ -155,7 +145,7 @@ class Room < ApplicationRecord
# Deletes all recordings associated with the room. # Deletes all recordings associated with the room.
def delete_all_recordings 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? delete_recording(record_ids) unless record_ids.empty?
end end

View File

@ -20,7 +20,6 @@ require 'bbb_api'
class User < ApplicationRecord class User < ApplicationRecord
rolify rolify
include ::APIConcern
include ::BbbApi include ::BbbApi
attr_accessor :reset_token attr_accessor :reset_token
@ -118,31 +117,8 @@ class User < ApplicationRecord
end end
def self.admins_order(column, direction) def self.admins_order(column, direction)
order("#{column} #{direction}") # Arel.sql to avoid sql injection
end order(Arel.sql("#{column} #{direction}"))
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)
end end
# Activates an account and initialize a users main room # Activates an account and initialize a users main room

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

@ -13,6 +13,21 @@
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. # 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? %> <% if @role.present? %>
<%= render "shared/components/admins_tags" %> <%= render "shared/components/admins_tags" %>
<% end %> <% end %>
@ -146,3 +161,4 @@
</div> </div>
<%= render "shared/modals/invite_user_modal" %> <%= 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="row">
<div class="col-lg-3 mb-4"> <div class="col-lg-3 mb-4">
<div class="list-group list-group-transparent mb-0"> <%= render "admins/components/menu_buttons" %>
<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>
</div> </div>
<div id="users" class="col-lg-9">
<%= render "admins/components/setting_view", setting_id: "users", setting_title: t("administrator.users.title"), search: true %>
<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> </div>
</div> </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

@ -89,7 +89,7 @@
<%= render "shared/components/room_block", room: room %> <%= render "shared/components/room_block", room: room %>
<% end %> <% end %>
</div> </div>
<%= render "shared/modals/delete_room_modal", room: room %> <%= render "shared/modals/delete_room_modal", recording_count: room.recording_count, room: room %>
<% end %> <% end %>
<% end %> <% end %>
<% unless room_limit_exceeded %> <% unless room_limit_exceeded %>

View File

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

View File

@ -20,17 +20,17 @@
<div class="col-lg-3 mb-4"> <div class="col-lg-3 mb-4">
<div class="list-group list-group-transparent mb-0"> <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") %> <span class="icon mr-3"><i class="fas fa-user"></i></span><%= t("settings.account.title") %>
</button> </button>
<% if @user.social_uid.nil? %> <% 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") %> <span class="icon mr-3"><i class="fas fa-lock"></i></span><%= t("settings.password.title") %>
</button> </button>
<% end %> <% 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") %> <span class="icon mr-3"><i class="fas fa-trash-alt"></i></span><%= t("settings.delete.title") %>
</button> </button>
</div> </div>

View File

@ -73,6 +73,9 @@ en:
registration_method_updated: Registration method successfully updated registration_method_updated: Registration method successfully updated
settings: Site Settings successfully changed settings: Site Settings successfully changed
unauthorized: You are not authorized to perform actions on this user 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 title: Organization Settings
users: users:
invite: Invite User invite: Invite User

View File

@ -37,6 +37,8 @@ Rails.application.routes.draw do
resources :admins, only: [:index] resources :admins, only: [:index]
scope '/admins' do 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 '/branding', to: 'admins#branding', as: :admin_branding
post '/coloring', to: 'admins#coloring', as: :admin_coloring post '/coloring', to: 'admins#coloring', as: :admin_coloring
post '/room_authentication', to: 'admins#room_authentication', as: :admin_room_authentication post '/room_authentication', to: 'admins#room_authentication', as: :admin_room_authentication

View File

@ -12,15 +12,9 @@ module BbbApi
end end
# Sets a BigBlueButtonApi object for interacting with the API. # Sets a BigBlueButtonApi object for interacting with the API.
def bbb def bbb(user_provider)
if Rails.configuration.loadbalanced_configuration if Rails.configuration.loadbalanced_configuration
if instance_of? Room user_domain = retrieve_provider_info(user_provider)
# 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
BigBlueButton::BigBlueButtonApi.new(remove_slash(user_domain["apiURL"]), user_domain["secret"], "0.8") BigBlueButton::BigBlueButtonApi.new(remove_slash(user_domain["apiURL"]), user_domain["secret"], "0.8")
else else

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

@ -52,7 +52,7 @@ describe AdminsController, type: :controller do
get :edit_user, params: { user_uid: @user.uid } get :edit_user, params: { user_uid: @user.uid }
expect(response).to render_template(:index) expect(response).to render_template(:edit_user)
end end
end end
@ -197,7 +197,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Branding Image") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Branding Image")
expect(feature[:value]).to eq(fake_image_url) 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
end end
@ -214,7 +214,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color")
expect(feature[:value]).to eq(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 end
it "changes the primary-lighten on the page" do it "changes the primary-lighten on the page" do
@ -229,7 +229,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Lighten") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Lighten")
expect(feature[:value]).to eq(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 end
it "changes the primary-darken on the page" do it "changes the primary-darken on the page" do
@ -244,7 +244,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Darken") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Primary Color Darken")
expect(feature[:value]).to eq(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 end
end end
end end
@ -264,7 +264,7 @@ describe AdminsController, type: :controller do
expect(feature[:value]).to eq(Rails.configuration.registration_methods[:invite]) expect(feature[:value]).to eq(Rails.configuration.registration_methods[:invite])
expect(flash[:success]).to be_present expect(flash[:success]).to be_present
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admin_site_settings_path)
end end
it "does not allow the user to change to invite if emails are off" do it "does not allow the user to change to invite if emails are off" do
@ -277,7 +277,7 @@ describe AdminsController, type: :controller do
post :registration_method, params: { method: "invite" } post :registration_method, params: { method: "invite" }
expect(flash[:alert]).to be_present expect(flash[:alert]).to be_present
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admin_site_settings_path)
end end
end end
@ -293,7 +293,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Authentication") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Authentication")
expect(feature[:value]).to eq("true") expect(feature[:value]).to eq("true")
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admin_site_settings_path)
end end
end end
@ -309,7 +309,7 @@ describe AdminsController, type: :controller do
feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Limit") feature = Setting.find_by(provider: "provider1").features.find_by(name: "Room Limit")
expect(feature[:value]).to eq("5") expect(feature[:value]).to eq("5")
expect(response).to redirect_to(admins_path) expect(response).to redirect_to(admin_site_settings_path)
end end
end end
end end

View File

@ -28,6 +28,8 @@ def random_valid_room_params
end end
describe RoomsController, type: :controller do describe RoomsController, type: :controller do
it_behaves_like "recorder"
include Recorder
describe "GET #show" do describe "GET #show" do
before do before do
@user = create(:user) @user = create(:user)
@ -39,7 +41,7 @@ describe RoomsController, type: :controller do
get :show, params: { room_uid: @owner.main_room } 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?) expect(assigns(:is_running)).to eql(@owner.main_room.running?)
end end

View File

@ -302,6 +302,7 @@ describe UsersController, type: :controller do
describe "PATCH #update" do describe "PATCH #update" do
it "properly updates user attributes" do it "properly updates user attributes" do
user = create(:user) user = create(:user)
@request.session[:user_id] = user.id
params = random_valid_user_params params = random_valid_user_params
patch :update, params: params.merge!(user_uid: user) patch :update, params: params.merge!(user_uid: user)
@ -315,6 +316,7 @@ describe UsersController, type: :controller do
it "renders #edit on unsuccessful save" do it "renders #edit on unsuccessful save" do
@user = create(:user) @user = create(:user)
@request.session[:user_id] = @user.id
patch :update, params: invalid_params.merge!(user_uid: @user) patch :update, params: invalid_params.merge!(user_uid: @user)
expect(response).to render_template(:edit) expect(response).to render_template(:edit)

View File

@ -133,420 +133,6 @@ describe Room, type: :model do
end end
context "#recordings" do 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 it "deletes the recording" do
allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:delete_recordings).and_return( allow_any_instance_of(BigBlueButton::BigBlueButtonApi).to receive(:delete_recordings).and_return(
returncode: true, deleted: true 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") .to raise_exception(ActiveRecord::RecordInvalid, "Validation failed: Email can't be blank")
end end
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 end